The Guide to Designing and Quality Controlling Payment Logic on Stripe

Updated 25 May 2023

12 Min

3289 Views

Follow

Share

The implementation of payment logic is important for any product that one way or another deals with transactions. A well-designed payment architecture in couple with a proper testing can save you a lot of time in future.

However, it may take too long to master the proficiency level of working with popular online payment gateways. That's the reason we're writing this guidance on designing payment logic on Stripe.

We'd like to help you better understand this payment gateway. For this, we've prepared interesting use cases, examples from our projects, and a bit of theory with code samples.

This guide is especially useful for QA engineers as they'll understand how to test payment logic based on Stripe. However, Project Managers and developers will also find lots of interesting information here.

How does it work?

A scheme of simple payment process on Stripe

Simple payment scheme

The scheme above is relevant for the case when users purchase content through a website or mobile application. It allows paying for the content seamlessly as users don't have to register or link their credit cards with their profiles. All they need to do is enter their credit card credentials and the rest happens behind the scenes:

  • Credentials are delivered to Stripe;
  • Stripe tokenizes the received data and returns a token to the back-end;
  • Back-end creates a charge;
  • All this is again sent to Stripe which now shares the received data with payment systems;
  • Payment systems respond to Stripe and tell it whether everything alright or report about problems;
  • Server gets a response from Stripe reporting about a state of the transaction.  

Therefore, users get content if the transaction was successful or receive an error message in case something went wrong. This is the simplest scheme of work.

A while ago, you needed to have a merchant bank account to make the aforementioned process possible. With Stripe it becomes much easier but there are two obligatory conditions:

Regions of the world that are supported by Stripe payment gateway

Countries that are supported by Stripe

Technically, the only restriction Stripe has at the moment is it's geography. It covers entire North America and Australia as well as a part of Europe, South America, and some other countries.

This restriction concerns only to business owners and isn't applicable to consumers. They can still pay for content (and other stuff) that's sold by a merchant wherever they are in the world. While entrepreneurs are able to get payouts only in case their bank account has been opened in one of those 25 counties.

How to connect a card

The procedure of linking your product user with Stripe customer should be done on the server-side and it looks like this:

  • 1.Credit card credentials are sent to Stripe from your app or website.
  • 2. Stripe returns a token which goes to the back-end.
  • 3. Back-end sends it back to Stripe.
  • 4. Stripe checks whether such customer exists (if yes, the card is added; if no, the new customer is created and then the card is added). 

The first added card is set as a default payment method with the help of which a transaction will be made.

Connect with account

In order for someone could get payments in your application (e.g. Uber drivers), they first have to add an account. Stripe operates with three types of accounts:

  • Standard account. This is an already existing account containing all the required credentials. It's registered by the user and validated by Stripe and a bank. 
  • Express account. This type enables easy onboarding as you can create an account on your own and all that remains for the user is to fill in some necessary fields. Note: This account works within the US only. 
  • Custom account. This account provides the greatest level of flexibility as it allows you to modify multiple parameters. However, at the same time, you as a platform are responsible for every interaction with your users. 

Core features of Stripe

Let's now consider the core features of this payment processor.

Charges

There are two types of charge: direct and destination.

Direct charge

Your platform charges a certain amount from the user and that money goes directly to the linked account of your partner. Again, in the case with Uber, this money will be transferred to drivers. In turn, the direct charge implies that all the fees are paid by the linked account (the driver) and the platform itself charges a fixed percentage from that amount.

The direct charge scheme on Stripe

Scheme of a direct charge

Destination charge

All the fees are paid by the platform and the net worth is kept on your platform's account. The money is transferred to the Stripe account of your platform afterward the automatic transfer is sent to the partner.

Auth and capture

Stripe has a support for two-step payments which enable you to authorize a charge first and capture it later. In other words, the authorized payments are guaranteed by card issuers and the requested amount is frozen on the customer card. In case the charge isn't captured for this period of time, the authorization is canceled and money is unfrozen.

Uber is a bright example of auth and capture. Users see an approximate cost of their trip when they book a ride. This cost is frozen on their credit card until they finish the ride. Afterward, Uber calculates the final price of the trip and charges it from the card.

This allows app owners to be on the safe side since the app knows for sure whether one or another user is solvent and if there are some problems which may block the transaction.

Transfers

Transfers can be made from the platform account to suppliers. For example, Uber drivers link Stripe accounts with their profiles to get payouts.

Subscriptions

Stripe also supports subscriptions. This feature is very flexible and enables you to set various intervals, different trial periods and do much more to adjust the subscription for your needs.

Refunds

The support for refunds is also present. If customers want to cancel their order, money can be easily refunded to their cards.

Dealing with objects in Stripe

It's time to consider the Stripe objects in more detail.

Source object

A checklist for the source object

KeyExpected value

id

stripe_id of added card

last4

last 4 numbers of added card

brand

credit card company (Visa, AE, etc.)

exp_month, exp_year

expiration date

customer

your customer stripe id

This object stores a payment method with the help of which a charge will be completed. On the scheme we've considered before, we simply created a source object of the card which isn't linked to any concrete user and so the charge can be made from it. Besides, the source object can be linked with users what gives them an opportunity to store all the payment methods there.

During testing, it's really important to make sure a payment method corresponds with the returned value. For this, you can check last4 and exp_month/year. In case the source object is linked with the specific customer and you want to ascertain it belongs to the right user, you can check the customer id.

Here is a JSON of this object:

{
        "id": "card_1CboP4CLud4t5fBlZMiVrzBq",
        "object": "card",
        "address_city": null,
        "address_country": null,
        "address_line1": null,
        "address_line1_check": null,
        "address_line2": null,
        "address_state": null,
        "address_zip": null,
        "address_zip_check": null,
        "brand": "Visa",
        "country": "US",
        "customer": "cus_D1s9PQgvr6U46j",
        "cvc_check": "pass",
        "dynamic_last4": null,
        "exp_month": 4,
        "exp_year": 2024,
        "fingerprint": "soMjdt25OvcMcObY",
        "funding": "credit",
        "last4": "4242",
        "metadata": {},
        "name": null,
        "tokenization_method": null
      }

Customer object

A checklist for the customer object

KeyExpected value

id

customer stripe_id

default_source

stripe_id of the default card

sources

the list of Sources

subscriptions

the list of Subscriptions (check if you use subscriptions)

The name of this object speaks for itself. It stores all the payment methods including the default one. The customer object contains information about users and their subscriptions. Stripe recalls users' credit cards and the default payment method that's chosen by them so you can charge users based on this data. The same thing with subscriptions -- Stripe manages them and withdraw subscription fees automatically.

It's possible to create webhooks for all the event in Stripe so the server could find out when the charge was made or when some error took place.

{
  "id": "cus_D1s9PQgvr6U46j",
  "object": "customer",
  "account_balance": 0,
  "created": 1528717303,
  "currency": null,
  "default_source": "card_1CboP4CLud4t5fBlZMiVrzBq",
  "delinquent": false,
  "description": null,
  "discount": null,
  "email": null,
  "invoice_prefix": "4A178DE",
  "livemode": false,
  "metadata": {},
  "shipping": null,
  "sources": {
    "object": "list",
    "data": [
      {
        "id": "card_1CboP4CLud4t5fBlZMiVrzBq",
        "object": "card",
        "address_city": null,
        "address_country": null,
        "address_line1": null,
        "address_line1_check": null,
        "address_line2": null,
        "address_state": null,
        "address_zip": null,
        "address_zip_check": null,
        "brand": "Visa",
        "country": "US",
        "customer": "cus_D1s9PQgvr6U46j",
        "cvc_check": "pass",
        "dynamic_last4": null,
        "exp_month": 4,
        "exp_year": 2024,
        "fingerprint": "soMjdt25OvcMcObY",
        "funding": "credit",
        "last4": "4242",
        "metadata": {},
        "name": null,
        "tokenization_method": null
      },
      {
        "id": "card_1CcC3uCLud4t5fBlW2UMknUW",
        "object": "card",
        "address_city": null,
        "address_country": null,
        "address_line1": null,
        "address_line1_check": null,
        "address_line2": null,
        "address_state": null,
        "address_zip": null,
        "address_zip_check": null,
        "brand": "Visa",
        "country": "US",
        "customer": "cus_D1s9PQgvr6U46j",
        "cvc_check": "pass",
        "dynamic_last4": null,
        "exp_month": 4,
        "exp_year": 2024,
        "fingerprint": "soMjdt25OvcMcObY",
        "funding": "credit",
        "last4": "4242",
        "metadata": {},
        "name": null,
        "tokenization_method": null
      }
    ],
    "has_more": false,
    "total_count": 2,
    "url": "/v1/customers/cus_D1s9PQgvr6U46j/sources"
  },
  "subscriptions": {
    "object": "list",
    "data": [],
    "has_more": false,
    "total_count": 0,
    "url": "/v1/customers/cus_D1s9PQgvr6U46j/subscriptions"
  }
}

Charge object

A checklist for the charge object

KeyExpected value

id

charge stripe_id

amount

payment amount in cents

amount_refunded

refunded amount in cents

customer

customer_id of a payer

captured

true - payment is made, false - payment is authorized

destination

stripe account of payee

amount

During testing, it's important to check the amount that was charged from the user. The amount is represented in cents, euro cents, etc.

amount_refunded

This field will have a value different from zero only if the whole amount of transaction (or a part of it) was refunded to the customer.

customer

The id of your customer is stored here.

captured

This key indicates a status of the transaction. The money can be held on the user credit card or can be charged.

destination

If you make use of destination charges, then the destination key will store a Stripe account of the user you've sent that money to.

"fingerprint": "soMjdt25OvcMcObY",
    "funding": "credit",
    "last4": "4242",
    "metadata": {},
    "name": null,
    "tokenization_method": null
  },
  "source_transfer": null,
  "statement_descriptor": null,
  "status": "succeeded",
  "transfer_group": null
}

Refund object

A checklist for the refund object

KeyExpected value

id

refund stripe_id

amount

payment amount in cents

status

success / pending / failed

Refund object can be embedded in the charge object if any part of the payment or the whole payment was refunded to a customer.

{
  "id": "re_1CcY10CLud4t5fBlN23KtYq7",
  "object": "refund",
  "amount": 999,
  "balance_transaction": "txn_1CcY10CLud4t5fBlhlmzzJuK",
  "charge": "ch_1CcD7dCLud4t5fBlC1srZNIB",
  "created": 1528892634,
  "currency": "usd",
  "metadata": {},
  "reason": null,
  "receipt_number": null,
  "status": "succeeded"
}

Transfer object

A checklist for the transfer object

KeyExpected value

id

transfer_id

amount

payout amount in cents

destination

linked account of a payee

reversed

false - money transaction, true - reverse of funds

reversals

list of reverse transfer objects

This object stores the data related to transfers from the platform balance to other accounts. In other words, all the payouts to your partners (e.g. Uber drivers) will be recorded and stored in this object.

Remember that all transactions should be loginized in the database. This makes testing easier as you can see the transfer id, go to Stripe, and make a query to look through parameters for checking:

amount

The amount that will be paid to a payee.

destination

Destination unveils a Stripe account of the user to whom a payment will be sent.

reversed

Sometimes it's necessary to cancel a transaction. This key acts as an indicator showing a false value if the transaction succeeded and a true value if it's reversed.

reversals

This key stores a list of the objects in case any part of your transfer was reversed.

{
  "id": "tr_1CcApyCLud4t5fBlZyx5mEPI",
  "object": "transfer",
  "amount": 250,
  "amount_reversed": 0,
  "balance_transaction": "txn_1CcApyCLud4t5fBlfA5cgXBz",
  "created": 1528803538,
  "currency": "usd",
  "description": null,
  "destination": "acct_18bAS3KcT341ksb9",
  "destination_payment": "py_1CcApyKcT341ksb9VawxIJdS",
  "livemode": false,
  "metadata": {},
  "reversals": {
    "object": "list",
    "data": [],
    "has_more": false,
    "total_count": 0,
    "url": "/v1/transfers/tr_1CcApyCLud4t5fBlZyx5mEPI/reversals"
  },
  "reversed": false,
  "source_transaction": null,
  "source_type": "card",
  "transfer_group": null
}

Balance Transaction object

A checklist for the Balance Transaction object

KeyExpected value

id

refund stripe_id

amount

payment amount in cents (pay attention to +/- signs)

available_on

date when money will be available for a payee

fee

amount of Stripe fee

fee_details

list of fee objects

net

amount of net income/expenditure

status

current status of operation

type

type of transaction (charge, refund, transfer)

This object contains information about any changes to the application balance. Basically, you don't need to test this object, but you have to understand where fees come from.

amount

The payment amount in cents. It can be followed by a minus or plus which indicate the process of sending and receiving funds respectively.

available_on

The money sent to partners will be available for them through a certain period of time. This key contains timestamps telling when the fund will be available.

fee

An amount of the Stripe fee.

fee_details

The list of fee objects with a detailed description of why one or another fee was charged.

net

The amount of net income.

status

The status of operation success.

type

This key stores a type of the object (charge, refund, transfer).

Balance transaction for transfer:

{
  "id": "txn_1CcApyCLud4t5fBlfA5cgXBz",
  "object": "balance_transaction",
  "amount": -250,
  "available_on": 1528803538,
  "created": 1528803538,
  "currency": "usd",
  "description": null,
  "exchange_rate": null,
  "fee": 0,
  "fee_details": [],
  "net": -250,
  "source": "tr_1CcApyCLud4t5fBlZyx5mEPI",
  "status": "available",
  "type": "transfer"
}

Balance transaction for charge:

{
  "id": "txn_1CbrRTCLud4t5fBlhRfMLdq1",
  "object": "balance_transaction",
  "amount": 10000,
  "available_on": 1529280000,
  "created": 1528728983,
  "currency": "usd",
  "description": "Charge user asdf11@example.com for instructor sodom@example.com lesson id: 77",
  "exchange_rate": null,
  "fee": 320,
  "fee_details": [
    {
      "amount": 320,
      "application": null,
      "currency": "usd",
      "description": "Stripe processing fees",
      "type": "stripe_fee"
    }
  ],
  "net": 9680,
  "source": "ch_1CbrP3CLud4t5fBlztHMxVzv",
  "status": "pending",
  "type": "charge"
}

Subscription object

A checklist for the Subscription object

KeyExpected value

id

subscription stripe_id

application_fee_percent

% that is charged by the app for subscription

billing

automatic charge or sending invoice

billing_cycle_anchor

time of the next cycle of subscription

current_period_start current_period_end

timeframes of current subscription period

plan

set of rules for subscription: amount, interval, number of trial days

This object contains information about the customer subscription. If you need to test Stripe subscriptions, then you should look through the subscription object and check the following:

application_fee_percent

This is a percent of the overall amount the app charges as soon as the subscription is made. The rest is paid to the content owner.

billing

This key is responsible for how the billing process is organized. Everything can be done automatically or manually through the invoice.

billing_cycle_anchor

It contains the due date of the next payment for renewal of the subscription.

current_period_start & current_period_end

The validity period of the customer subscription.

plan

It stores the object of a subscription plan. This key includes a set of rules like the amount one should pay for the subscription, interval, number of trial days, etc.

{
  "id": "sub_D2JskPBqcW24hu",
  "object": "subscription",
  "application_fee_percent": null,
  "billing": "charge_automatically",
  "billing_cycle_anchor": 1528820423,
  "cancel_at_period_end": false,
  "canceled_at": null,
  "created": 1528820423,
  "current_period_end": 1531412423,
  "current_period_start": 1528820423,
  "customer": "cus_D2Jsi3JgT5zPh1",
  "days_until_due": null,
  "discount": null,
  "ended_at": null,
  "items": {
    "object": "list",
    "data": [
      {
        "id": "si_D2Js7N4mYxzAaY",
        "object": "subscription_item",
        "created": 1528820424,
        "metadata": {
        },
        "plan": {
          "id": "ivory-express-917",
          "object": "plan",
          "active": true,
          "aggregate_usage": null,
          "amount": 999,
          "billing_scheme": "per_unit",
          "created": 1528819224,
          "currency": "usd",
          "interval": "month",
          "interval_count": 1,
          "livemode": false,
          "metadata": {
          },
          "name": "Ivory Express",
          "nickname": null,
          "product": "prod_D2JYysdjdQ2gwT",
          "statement_descriptor": null,
          "tiers": null,
          "tiers_mode": null,
          "transform_usage": null,
          "trial_period_days": null,
          "usage_type": "licensed"
        },
        "quantity": 1,
        "subscription": "sub_D2JskPBqcW24hu"
      }
    ],
    "has_more": false,
    "total_count": 1,
    "url": "/v1/subscription_items?subscription=sub_D2JskPBqcW24hu"
  },
  "livemode": false,
  "metadata": {
  },
  "plan": {
    "id": "ivory-express-917",
    "object": "plan",
    "active": true,
    "aggregate_usage": null,
    "amount": 999,
    "billing_scheme": "per_unit",
    "created": 1528819224,
    "currency": "usd",
    "interval": "month",
    "interval_count": 1,
    "livemode": false,
    "metadata": {
    },
    "name": "Ivory Express",
    "nickname": null,
    "product": "prod_D2JYysdjdQ2gwT",
    "statement_descriptor": null,
    "tiers": null,
    "tiers_mode": null,
    "transform_usage": null,
    "trial_period_days": null,
    "usage_type": "licensed"
  },
  "quantity": 1,
  "start": 1528820423,
  "status": "active",
  "tax_percent": null,
  "trial_end": null,
  "trial_start": null
}

Use cases

Let's consider the use of Stripe capabilities for building business logic.

Subscriptions

Description: Users pay $10/month for using the content. The author of the content earns 80% of the overall subscription cost. Users have 7 trial days.

Implementation:

  • 1. Create the subscription plan in Stripe where you specify cost, % of app fee, and interval.
  • 2. Integrate webhooks in order for the server could understand when someone subscribes and when the charge is made.
  • 3. Integrate emails to send users an invoice or receipt about successful payments.
  • 4. When a user purchases the subscription, Stripe counts down 7 days from that moment and makes a charge.
  • 5. Author gets money, the platform gets a fee.  

Fee: 2.9% + 30c

One-time content purchase

Description: Users can purchase a content on your website or app.

Implementation:

  • 1. The customer tokenizes card.
  • 2.Back-end executes Charge.
  • 3. If the Charge has been successful, the platform's business logic comes into play and allows the customer to get the purchased content. 

In this case, you'll pay the following fees: 2.9% from the Charge cost + 30 cents.

Uber-like app

Description: Customer pays for the service, the platform charges 20%, the driver gets 80%.

Preconditions:

  • Driver linked an account
  • User added a card 

This variant implies you create transfers on your own after the user has paid. For this, you should authorize the payment during booking of the ride and perform a capture as soon as the ride is finished. Afterward, you create a transfer for the driver that's equal to 80% of the total amount, pay the Stripe fee, and the rest is your net income.

Fee: 2.9% + 30 cents

Uber-like app #2

Apps like Uber are the perfect example to describe Stripe use cases, so let's consider one more variation.

Description: Customer pays for the service, the platform charges 20%, the driver gets 80%. In addition, the driver can pay $5 for the priority booking right.

Preconditions:

  • Driver linked an account
  • User added a card

Variant #1

You charge $5 from the driver because the priority option is chosen, authorize payment for the customer, perform a capture as the ride ends, create a transfer for the driver, and keep the rest. In this case, you pay a 2.9% fee and 30 cents for each charge (customer and driver = $1).

By the way, here is a checklist to help you estimate how much does it cost to develop an app like Uber.

Variant #2

You can escape from paying fees by creating the inner monetization on your platform. For example, you can introduce an in-app balance for the driver. This way when you receive money from the customer, you calculate the driver's share and then transfer those funds to the inner balance. Next, add the button for making payouts to the app or implement automatic payouts (e.g. once a week as Uber does).

Cashflow for the second example

Cashflow for the variant #2

Load and stability tests

You can also perform the load testing in order to check how many transactions Stripe can process and if there any rate limit. Such tool as GMeter will be in handy for you during testing these parameters.

So, how to test all this? Create the event launching the payment logic on the server-side. Next, you check the log of the transaction in your database and check it's status using GMeter (don't forget that all transactions have to be loginized in the database). Then, you can emulate all those actions from N-users and define an approximate number of successful operations per unit time (e.g. 10 operations a minute). As a result, you can conclude whether the logic optimization is necessary or not. The good thing is that this approach enables to test the combination of Stripe with the server-side logic of your platform.

Wrapping up

We hope this guide will help you in designing and testing Stripe-based payment logic! If you've liked this piece of writing, you can subscribe to our newsletter and get more useful content to your inbox once a week.

Also, feel free to contact our managers if you have some questions regarding cooperation with our company.

Author avatar...
About author

Evgeniy Altynpara is a CTO and member of the Forbes Councils’ community of tech professionals. He is an expert in software development and technological entrepreneurship and has 10+years of experience in digital transformation consulting in Healthcare, FinTech, Supply Chain and Logistics

Rate this article!
2631 ratings, average: 4.71 out of 5

Give us your impressions about this article

Give us your impressions about this article

Latest articles
Start growing your business with us
By sending this form I confirm that I have read and accept the Privacy Policy