Stripe Payment Integration

At the time of writing this article, there is no official support of Stripe for React-Native. I, having knowledge of backend, was trying to create RESTful endpoints to integrate Stripe payment using Spring boot. Although the documentation of Stripe is aesthetically designed, I really had a tough time doing it. I find nothing useful for React-Native in the docs.

There are three ways by which we can initiate a payment using Stripe payment.

  1. Stripe Checkout
  2. Charges API
  3. PaymentIntent API

Stripe Checkout redirects customers to the prebuilt page and accepts payment. This is not what I wanted for my mobile application. Charges API was ok for me but it is recommended for the businesses in the USA and Canada. More importantly, it’s not SCA ready (Strong Customer Authentication) and only works for card payments. The only option left was to use PaymentIntent API.

In this article, I am going to explain the backend and frontend (React-Native tipsi-stripe library) flow for payment using Stripe.

  1. The user inputs the card details.
  2. The frontend (React-Native) will call the Stripe API to create PaymentMethod object using the card details (Card-number, Expiry date, CVC).
  3. The frontend will send the Payment Method Id created from the response of Step-2 along with currency and amount to the backend.
  4. The backend will create the Payment Intent Object using the payment method Id.
  5. If the status of the PaymentIntent is requires_action, there is secure 3D authentication enabled for the payment and needs further processing. Hence, the backend will send payment intent client_secret to the frontend.
  6. The frontend will use this client_secret and proceed for further authentication and payment from the frontend per se.
  7. If the status of the PaymentIntent is other than requires_action, the backend will complete the transaction.
  8. The backend will receive the subscribed events from the Stripe server using Webhooks. The backend then sends the Push Notification using Expo to the frontend notifying the success and failure of the payment.

Let’s see the code snippets. I used stripe-java library to communicate with the Stripe server.

The dependency in pom.xml looks like:

<dependency>
    <groupId>com.stripe</groupId>
    <artifactId>stripe-java</artifactId>
    <version>${stripe.version}</version>
</dependency>

The createPayment method looks like this:

Stripe.apiKey = this.secretKey;
    PaymentIntentCreateParams params = PaymentIntentCreateParams.builder()
            .setCurrency(currency)
            .setAmount(getConvertedAmount(amount))
            .setPaymentMethod(paymentMethodId)
            .setConfirm(true)              
            .setReceiptEmail(SecurityContext.getUser().getUsername())         
.setConfirmationMethod(PaymentIntentCreateParams.ConfirmationMethod.AUTOMATIC)
.build();

PaymentIntent paymentIntent = createPaymentIntent(params);
if (REQUIRES_ACTION.equals(paymentIntent.getStatus())) {
        //requires 3D secure authentication in frontend
 return paymentIntent.getClientSecret();
    } else {
        return EMPTY;
    }
}

private PaymentIntent createPaymentIntent(PaymentIntentCreateParams params) {
    try {
        return PaymentIntent.create(params);
    } catch (StripeException e) {
        //log
    }
}

The webhooks API will be called by Stripe and it can be configured from the Stripe Dashboard. You can choose the event types that you would like to receive in your webhook endpoint. For payment success and failure, I am listening to payment_intent.succeeded and payment_intent.payment_failed events respectively.

The webhooks API looks like:

@PostMapping(value = "/your-webhook-endpoing")
public ResponseEntity<Void> webhooks(@RequestBody String json, HttpServletRequest httpRequest) {

    Event event = validateStripeHeadersAndReturnEvent(json, httpRequest.getHeader("Stripe-Signature"));
    StripeObject stripeObject = getStripeObject(event);
    PaymentIntent paymentIntent = (PaymentIntent) stripeObject;
    this.stripePaymentService.handlePaymentSuccess(event.getType(), paymentIntent);
    this.stripePaymentService.handlePaymentFailure(event.getType(), paymentIntent);
    return ResponseEntity.status(HttpStatus.OK).build();
}

As you can see, I am getting Stripe-Signature header from the request because I want to validate if the request is being sent by the Stripe server or not. The validateStripeHeadersAndReturnEvent method looks like:

private Event validateStripeHeadersAndReturnEvent(String payload, String headers) {
    try {
        return Webhook._constructEvent_(
                payload, headers, this.webhookSecret
        );
    } catch (JsonSyntaxException e) {
        throw new UnAuthorizedException("Invalid payload");
    } catch (SignatureVerificationException e) {
        throw new UnAuthorizedException("Invalid Signature");
    }
}

You can write your own logic after you receive webhooks. I am sending the success and failure push notifications.

Thanks a lot for reading this article :)

No Comments Yet