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.
Podobne artykuły
Dlaczego warto poznać Syliusa i od czego zacząć jego naukę?
Jak od środka wygląda praca programisty w branży e-commerce
Czy .NET to wciąż dobry wybór dla systemów CMS i eCommerce w 2023 roku?
UGotIT – pierwszy konkurs z obszaru e-commerce dla programistów
Stabilny tandem dla rozwoju B2B w online – Magento 2 i AWS
Bezpieczeństwo sklepów internetowych opartych na platformie Magento 2
Praca programisty w e-commerce - jak to wygląda od środka?
