Python i Stripe. Jak zaimplementować system płatności online w e-sklepie
Zastanawiasz się nad własnym biznesem? Masz już witrynę internetową sklepu? Jeżeli tak, to świetnie trafiłeś! W tym artykule zdradzę Ci jak zaimplementować Stripe – jedną z najpopularniejszych platform do płatności online. Pssst… #python on the board!
Na początek, przedstawiam Wam jedną z najpopularniejszych platform do płatności internetowych – Stripe. Działa ona w ponad 25 krajach i nieustannie rozszerza swoją działalność o kolejne kraje. Umożliwia przyjmowanie przelewów, rozliczenia, wpłaty, a także dostarcza gotowy interfejs do zarządzania użytkownikami. Ty, jako właściciel sklepu internetowego, nie przechowujesz danych kart/kont bankowych swoich użytkowników – Stripe zapewnia całkowite wsparcie i zwalnia Cię z odpowiedzialności za wrażliwe dane.
Warto także podkreślić, że Stripe działa w oparciu o model prowizyjny – w zależności od kraju, pobierana jest odpowiednia kwota za udane obciążenie karty (w Polsce jest to 1,40% + 1 PLN od każdej transakcji).
Dla Ciebie, jako sprzedawcy, skorzystanie z płatności online oznacza naliczanie prowizji od transakcji: transferów, przelewów oraz regulowania należności kartą – jednym słowem zarabiasz. I co najważniejsze, Stripe udostępnia API, napisane w #python. Zabierajmy się za implementację!
Spis treści
Implementacja Stripe
Załóżmy, że podstawowe informacje o użytkownikach trzymamy w bazie (tabele stworzymy w Django). Model użytkownika (User), oprócz profilu (UserProfile), posiadać będzie także pole stripe_id, w którym przechowywać będziemy unikalne ID usera, wygenerowane przez Stripe.
class User(AbstractBaseUser): """ User model. """ USERNAME_FIELD = "email" REQUIRED_FIELDS = ["first_name", "last_name"] email = models.EmailField( verbose_name="E-mail", unique=True ) first_name = models.CharField( verbose_name="First name", max_length=30 ) last_name = models.CharField( verbose_name="Last name", max_length=40 ) city = models.CharField( verbose_name="City", max_length=40 ) stripe_id = models.CharField( verbose_name="Stripe ID", unique=True, max_length=50, blank=True, null=True ) objects = UserManager() @property def get_full_name(self): return f"{self.first_name} {self.last_name}" class Meta: verbose_name = "User" verbose_name_plural = "Users" class Profile(models.Model): """ User's profile. """ phone_number = models.CharField( verbose_name="Phone number", max_length=15 ) date_of_birth = models.DateField( verbose_name="Date of birth" ) postal_code = models.CharField( verbose_name="Postal code", max_length=10, blank=True ) address = models.CharField( verbose_name="Address", max_length=255, blank=True ) class Meta: abstract = True class UserProfile(Profile): """ User's profile model. """ user = models.OneToOneField( to=User, on_delete=models.CASCADE, related_name="profile", ) group = models.CharField( verbose_name="Group type", choices=GroupTypeChoices.choices(), max_length=20, default=GroupTypeChoices.CLIENT.name, ) def __str__(self): return self.user.email class Meta: unique_together = ("user", "group")
Mając już tak zdefiniowane wcześniej modele, możemy stworzyć testowych użytkowników:
# user 1 - employer user1, _ = User.objects.get_or_create( email="foo@bar.com", first_name="Employer", last_name="Testowy", city="Białystok", ) user1.set_unusable_password() group_name = "employer" _profile1, _ = UserProfile.objects.get_or_create( user=user1, date_of_birth=datetime.now() - timedelta(days=6600), group=GroupTypeChoices(group_name).name, address="Myśliwska 14", postal_code="15-569", phone_number="+48100200300", ) # user2 - employee user2, _ = User.objects.get_or_create( email="bar@foo.com", first_name="Employee", last_name="Testowy", city="Białystok", ) user2.set_unusable_password() group_name = "employee" _profile2, _ = UserProfile.objects.get_or_create( user=user2, date_of_birth=datetime.now() - timedelta(days=7600), group=GroupTypeChoices(group_name).name, address="Myśliwska 14", postal_code="15-569", phone_number="+48200300400", )
Mamy już wszystkie potrzebne informacje, stworzyliśmy dwóch użytkowników, możemy zacząć pracę ze Stripe.
Tworzenie kont
Wszystko teraz tak naprawdę zależy od tego, z jakim typem konta chcielibyśmy naszego użytkownika powiązać. Jeżeli nasz użytkownik będzie zlecał usługi (Employer – zleceniodawca), tworzymy konto Customera:
response_customer = stripe.Customer.create( email=user1.email, description=f"EMPLOYER - {user1.get_full_name}", name=user1.get_full_name, phone=user1.profile.phone_number, ) user1.stripe_id = response_customer.stripe_id user1.save()
Z kolei gdy nasz użytkownik będzie wykonywał usługi (Employee – zleceniobiorca), tworzymy konto Connected Account:
mcc_code, url = "1520", "https://www.softserveinc.com/" response_ca = stripe.Account.create( type="custom", country="PL", email=user2.email, default_currency="pln", business_type="individual", settings={"payouts": {"schedule": {"interval": "manual", }}}, requested_capabilities=["card_payments", "transfers", ], business_profile={"mcc": mcc_code, "url": url}, individual={ "first_name": user2.first_name, "last_name": user2.last_name, "email": user2.email, "dob": { "day": user2.profile.date_of_birth.day, "month": user2.profile.date_of_birth.month, "year": user2.profile.date_of_birth.year, }, "phone": user2.profile.phone_number, "address": { "city": user2.city, "postal_code": user2.profile.postal_code, "country": "PL", "line1": user2.profile.address, }, }, ) user2.stripe_id = response_ca.stripe_id user2.save()
W obu przypadkach API Stripe’a zwraca unikalny ID (id lub stripe_id), dzięki któremu możemy identyfikować naszych użytkowników.
Jeżeli tworzymy konto Customera, ID poprzedza prefix “cus_xxx”, a w przypadku Connected Account prefix “acct_xxx”.
O ile konto Customera nie wymaga dodatkowych kroków w kontekście rejestracji, to aby mieć w pełni zweryfikowane konto Connected Account, należy:
- zaakceptować politykę Stripe:
tos_acceptance = { "date": int(time.time()), "ip": user_ip } stripe.Account.modify( user2.stripe_id, tos_acceptance=tos_acceptance )
Przekazujemy tutaj w postaci timestamp aktualny czas (date) oraz adres IP użytkownika (ip).
- załączyć dokumenty potwierdzające tożsamość (paszport, dowód osobisty lub prawo jazdy):
passport_front = stripe.File.create( purpose="identity_document", file=_file, # ContentFile object stripe_account=user2.stripe_id, ) individual = { "verification": { "document": {"front": passport_front.get("id"),}, "additional_document": {"front": passport_front.get("id"),}, } } stripe.Account.modify(user2.stripe_id, individual=individual)
W tym przykładzie przekazujemy do weryfikacji paszport – oprócz frontowego zdjęcia dokumentu, należy jeszcze przekazać ten sam obiekt jako dodatkowy dokument. W innym wypadku weryfikacja się nie powiedzie…
To już wszystkie kroki, jeżeli chodzi o rejestrację klienta. Pora na dodanie karty do konta Customera:
new_card_source = stripe.Customer.create_source( user1.stripe_id, source=token ) stripe.SetupIntent.create( payment_method_types=["card"], customer=user1.stripe_id, description="some description", payment_method=new_card_source.id, )
W pierwszej kolejności dodajemy Customerowi nowe źródło płatności w postaci tokena, który zawiera zakodowane informacje o karcie. Następnie musimy ustawić wcześniej utworzoną metodę płatności jako domyślną (SetupIntent).
Przechodząc pomyślnie przez powyższe kroki, możemy spróbować ściągnąć kwotę z konta Customera:
payment_method = stripe.Customer.retrieve( user1.stripe_id ).default_source payment_intent = stripe.PaymentIntent.create( amount=amount, currency="pln", payment_method_types=["card"], capture_method="manual", customer=user1.stripe_id, payment_method=payment_method, application_fee_amount=application_fee_amount, transfer_data={"destination": user2.stripe_id}, # connected account description=description, metadata=metadata, ) payment_intent_confirm = stripe.PaymentIntent.confirm( payment_intent.stripe_id, payment_method=payment_method )
W powyższym przykładzie karta Customera jest obciążana o kwotę podaną w amount. Nasza prowizja to application_fee_amount
– tutaj musimy podać już wyliczoną kwotę prowizji, która powinna trafić na nasze konto.
Za docelowe konto (tam, gdzie mają trafić środki za wykonane zlecenie) odpowiada transfer_data
– w destination podajemy Stripe ID klienta (Connected Account).
Na koniec musimy potwierdzić obciążenie karty Customera (payment_intent_confirm
) – efektem będzie tymczasowa blokada kwoty na karcie Customera.
W momencie, gdy chcemy ściągnąć środki z karty Customera, robimy:
stripe.PaymentIntent.capture( payment_intent.id, amount_to_capture=amount )
Po wykonaniu tej metody środki powinny pojawić się na koncie klienta (Connected Account).
Aby sprawdzić jego balance, wpisujemy:
stripe.Balance.retrieve(stripe_account=user2.stripe_id)
W odpowiedzi uzyskamy informację o aktualnie dostępnych środkach na koncie:
- pending – w toku, co oznacza że środki nie są jeszcze dostępne do wypłaty,
- available – dostępne, co oznacza że środki można wypłacić teraz.
Może dojść czasem do sytuacji, w której będziemy musieli obciążyć jedną ze stron, np. za brak wykonanego zlecenia, a wcześniej obciążyliśmy już kartę Customera:
stripe.Charge.create( amount=amount, currency="pln", source=user2.stripe_id, description=description )
W ten sposób zleceniobiorca (Connected Account) może być obciążony kwotą podaną w amount
. Efektem jest zmniejszenie aktualnego balansu na koncie.
No i musimy zwrócić z powrotem środki zleceniodawcy (Customer):
stripe.PaymentIntent.cancel(payment_intent.id)
Podsumowanie
Stripe dostarcza API, dzięki któremu możemy w szybki i prosty sposób zbudować system wspierający płatności online. W zależności od naszego “modelu biznesowego”, w łatwy sposób jesteśmy w stanie dobrać odpowiednio zaimplementowane metody.
Dokumentacja API jest na tyle przejrzysta, że po odpowiedniej lekturze, zabieramy się za pisanie kilkulinijkowego kodu, który robi za nas całą robotę – oczywiście wcześniej musimy odpowiednio przetestować napisaną przez nas logikę, aby mieć pewność, że każda dowolna transakcja będzie przetwarzana wedle naszych oczekiwań. Służą temu tokeny testowe, a także testowe numery kont bankowych i kart, dzięki którym możemy wykonywać realne transakcje.
Po odpowiednio wykonanych testach, możemy przejść z trybu deweloperskiego na produkcyjny. Jedyną różnicą będzie klucz API, który będzie wykorzystywany przy każdym zapytaniu do Stripe.
Zdjęcie główne artykułu pochodzi z unsplash.com.