What I’m trying to do:
I am setting up Stripe payments for my app, and I have been able to successfully process transactions with the API (calling it from the client side). However, I need to get the credit card issuing country for tax reasons, and it seems the test API is returning that as “None” even though I use credit cards from given countries (e.g. Finland, Austria) as explained in the Stripe test API documentation (Test card numbers | Stripe Documentation).
What I’ve tried and what’s not working:
For instance trying with an Austrian credit card, on a successful transaction the return message is:
{'result': 'succeeded', 'charge_id': 'ch_XXX', 'source': {'exp_month': 12, 'exp_year': 2026, 'last4': '0008', 'address': {'line_1': None, 'line_2': None, 'zip': None, 'city': None, 'country': None, 'zip_check': None}, 'cvc_check': 'pass'}, 'url': 'https://dashboard.stripe.com/test/payments/ch_XXX'}
(The charge_ids have been edited to omit the actual transaction number.)
Now I understand that for a test card no actual address would be provided, but the country would be quite relevant for testing. Why else would Stripe offer test cards from different countries if they do not actually have any impact?
I am wondering if this is an issue with the way the Stripe API has been implemented on Anvil?
Code Sample:
import stripe.checkout
c = stripe.checkout.charge(currency="EUR", amount=price,email=anvil.server.call("GetUserEmail"))
2 Likes
I figured out the issue, In fact, the address information does not come from Stripe but is entered by the user during the payment transaction if the billing_address parameter is set to True. It seems Stripe checks this against the billing address on file for the card and returns zip_check ‘pass’ if they match.
To get the address the correct function call is:
import stripe.checkout
c = stripe.checkout.charge(currency="EUR", amount=price, billing_address=True, email=anvil.server.call("GetUserEmail"))
and when the user enters their billing adress, the API returns a transaction record:
{'result': 'succeeded', 'charge_id': 'ch_XXX', 'source': {'exp_month': 11, 'exp_year': 2025, 'last4': '4242', 'address': {'line_1': 'User Street Address', 'line_2': None, 'zip': 'User Zip Code', 'city': 'Helsinki', 'country': 'Finland', 'zip_check': 'pass'}, 'cvc_check': 'pass'}, 'url': 'https://dashboard.stripe.com/test/payments/ch_XXX'}
4 Likes
Continuing the topic, the above applies for client side transactions, where the country information needs to come from the customer. But the more secure implementation where the charging happens on server side has the desired behaviour, i.e. the issuing country of the card coming directly from Stripe.
Get the token on the client side, handling the exception if the user cancels the transaction:
try:
token, info = stripe.checkout.get_token(currency="EUR", amount=price)
except:
# user cancelled the purchase, give feedback if you like
And on the server side after you have passed the token and retrieved the user email e.g. from anvil.users.get_user()[“email”]
try:
stripe_customer = anvil.stripe.new_customer(email, token)
charge = stripe_customer.charge(amount=price, currency=currency)
except Exception as e:
# the reason for the exception (card declined etc.) can be retreived from
# str(e).split(";")[0]
The charge object now includes useful information, e.g.
charge['status']
charge["id"]
charge["payment_method_details"]["card"]["last4"]
charge["payment_method_details"]["card"]["country"]
3 Likes