DEV Community

Cover image for Python Payment Gateway Integration with Rapyd
Kyle Pollock for Rapyd

Posted on • Updated on • Originally published at community.rapyd.net

Python Payment Gateway Integration with Rapyd

By Nicholas Babu

Whether your business is online or on-premise, being flexible in how you collect payments can contribute greatly to your revenue and your reputation. When your customers have a good purchasing experience, you can earn their trust and loyalty, which encourages them to return to your business time and time again—and to tell their friends about you too.

Rapyd is a Fintech-as-a-Service platform that offers a variety of services, including issuing cards, handling subscriptions and recurring billing, and providing flexible payment options for personal or business use. With Rapyd, you can deposit or withdraw cash from various ATMS and over-the-counter cash access points across the globe or integrate your e-commerce store with their Collect API.

In this article, you'll learn how to integrate Rapyd Collect's Hosted Checkout workflow in a Python application.

The Rapyd Collect API

The Rapyd Collect API allows you to accept payments from anywhere in the world. To help you increase your reach and sales, it accommodates over a thousand payment method types under the following categories:

  • Card
  • Bank redirect
  • Bank transfer
  • eWallet
  • Cash

Rapyd Collect also allows you to handle subscriptions, invoicing, and payment modifications, such as issuing refunds or canceling transactions if necessary.

Rapyd is certified as a Level 1 service provider and has earned the British Standards Institution (BSI) information security certificate. Adherence to these industry security standards ensures that the Rapyd platform is very safe for handling sensitive information.

To make it easy to start collecting payment in your application, Rapyd provides ready-made plugins for some of the most popular e-commerce platforms like WooCommerce and Shopify. If those fail to meet your application setup needs, a custom integration with their APIs is simple too.

Rapyd Collect offers two checkout options: hosted and embedded. For Hosted Checkout, the user is redirected to Rapyd's website to complete the checkout process before being redirected back to a "success" URL, assuming everything went through without any issues. Embedded checkout allows your customers to securely check out from within your app without leaving your website. In this tutorial, you'll implement the Hosted Checkout workflow:

Rough architecture diagram for hosted checkout workflow

This workflow generally includes the following:

  • Your app makes a request to the /v1/checkout endpoint with the payment details, including the redirect URLs.
  • After the call to that endpoint is successful, the user is then redirected to Rapyd's hosted checkout page, which is related to the payment details used in step 1 in the diagram above.
  • From the checkout page, a user is either redirected to the complete_checkout_url or the cancel_checkout_url, as defined in the payment details in step 1 above, depending on the actions the user makes.

Implementing Payment in a Python Application with Rapyd

Before you get started, here are a few prerequisites that must be completed to follow along with the tutorial:

  • Download and install Python 3 on your machine.
  • Create a Rapyd account and follow the prompts to complete the activation process. Refer to the guide Signing Up for a Client Portal Account for more details. Then proceed to the steps to finish setting things up.

From your Rapyd dashboard, switch to Sandbox mode, select the Developers icon on the left pane, and click on the Credentials Details tab. Note down the access key and the secret key as these are required to make API calls to Rapyd's endpoints. You'll also be required to update these keys when you switch to production mode when you're finished developing/testing your application and are ready to start collecting real cash from your customers. You will find that there are two sets of access and secret keys when you switch from sandbox mode (sandbox credentials) to production mode (production credentials).

Steps to follow to access your app's credentials

From your dashboard, you can customize the checkout page for your organization by changing the logo and color, editing accepted payment methods and card types, adding and inviting users/roles, adding a webhook endpoint, and so on.

To add some basic customization, click on the settings cog on the bottom left side and then select Branding to reveal an interface where you can customize your checkout page. Here, you can add your brand's logo, change the color or text of the payment action button, adjust accepted payment methods, and more. Be sure to make edits to this page as you see fit and click Save before moving on to create the Python app.

Brand customization dashboard

Python App Setup

In this tutorial, you'll be using the Flask framework to create a Python web application. This app will contain three files:

  • app.py: contains app route implementations
  • utilities.py: contains a reusable make_request method you can call from anywhere within your app, passing in the right arguments
  • requirements.txt: contains the app dependencies

To get started, create a new folder named events, which will serve as your project folder. Inside it, create the file requirements.txt and add the following contents:

flask==2.2.2
Enter fullscreen mode Exit fullscreen mode

Next, create the file app.py and paste the following code inside:

from flask import Flask, request, redirect
import utilities

app = Flask(__name__)


@app.route('/', methods=['GET', 'POST'])
def app_home():
    # payment form submissions (payment-form name) in html below.
    if request.method == 'POST' and request.form['amount']:
        amount = int(request.form['amount'])
        body = {
            'amount': amount,
            'complete_checkout_url': 'http://example.com/complete',
            'country': 'US',
            'currency': 'USD',
            'cancel_checkout_url': 'http://example.com/cancel',
            'language': 'en',
        }

        try:
            # Generate checkout with this payment object
            req = utilities.make_request('post', '/v1/checkout', body)
            print('++++++ payment created +++++')
            print(req['data']['redirect_url'])
            print('++++++ payment created +++++')

            # Redirect to checkout.
            return redirect(req['data']['redirect_url'])

        except Exception as ex:
            print(ex)

    # Checkout html
    checkout_markup = "<div> <h1>Welcome to my Website!</h1> <hr>" \
                    "<h1> Checkout my upcoming event</h1> " \
                    "<section><h3>Consuming 3rd party apis (online)</h3> " \
                    "<p>Introduction to Postman Collections and api examples</p>" \
                    "<bold>$100</bold> <p></p>" \
                    "<form name=payment-form method=POST> " \
                    "<input type=hidden name=amount value=100>" \
                    " <input type=submit value=\"Reserve your slot \" name=submit>" \
                    "</form>" \
                    "</section><hr>" \
                    "</div>"
    return checkout_markup


@app.route('/rapyd-webhooks', methods=['POST'])
def rapyd_webhooks():
    print("Data received from Rapyd Webhook event is : ", request.json)
    return "Data received"

app.run(debug=True)
Enter fullscreen mode Exit fullscreen mode

The app.py file above defines the app's homepage, which will contain the payment form with an input field named "amount" and an explicit value set to 100. In a real-world scenario, this number would come from the product's total price calculations.

When the form is submitted, the following things take place:

  • The form posts to the same URL, and from the POST request, you get the amount value as defined in the form.
  • This amount is used to create a dictionary that contains details about the payment item to generate a checkout form, including the currency and redirect URLs.
  • Then the app makes a request to the /v1/checkout endpoint using the utilities make_request method to pass in the details above. On success, it redirects you to the hosted checkout page that features the customizations you set earlier.

Finally, create the utilities.py file. To make requests to Rapyd's API endpoints, it's a requirement that the request object is correctly formatted, including the required headers and signatures. Rapyd helps you do this efficiently using utilities. You'll simply copy the code under the Python tab and paste it in the utilities.py file, then set the values of the access_key and secret_key variables to those specific to your app under the Developers tab on your Rapyd dashboard.

(Note: Later, when you switch from sandbox to production, don't forget to update the same values accordingly. You'll also need to update the base URL to the production one to avoid running into issues.)

import json
import random
import string
import hmac
import base64
import hashlib
import time
import requests

"""
Replace base_url, access_key and secret key
accordingly (sandbox vs production)
"""
base_url = 'https://sandboxapi.rapyd.net'
secret_key = '<secret key>'
access_key = '<access key>'

"""
Generates and returns a unique random salt.
"""


def generate_salt(length=12):
    return ''.join(random.sample(string.ascii_letters + string.digits, length))


"""
Returns the current time in seconds.
"""


def get_unix_time(days=0, hours=0, minutes=0, seconds=0):
    return int(time.time())


"""
Generates a signature in relation to, the random salt, current time, http
method, path, the request body and the app keys.
"""


def update_timestamp_salt_sig(http_method, path, body):
    if path.startswith('http'):
        path = path[path.find(f'/v1'):]
    salt = generate_salt()
    timestamp = get_unix_time()
    to_sign = (http_method, path, salt, str(timestamp), access_key, secret_key, body)

    h = hmac.new(secret_key.encode('utf-8'), ''.join(to_sign).encode('utf-8'), hashlib.sha256)
    signature = base64.urlsafe_b64encode(str.encode(h.hexdigest()))
    return salt, timestamp, signature


"""
The current header signature dictionary.
"""


def current_sig_headers(salt, timestamp, signature):
    sig_headers = {'access_key': access_key,
                'salt': salt,
                'timestamp': str(timestamp),
                'signature': signature,
                'idempotency': str(get_unix_time()) + salt}
    return sig_headers


"""
Formats the request body and returns it, and the unique salt, signature and current timestamp.
"""


def pre_call(http_method, path, body=None):
    str_body = json.dumps(body, separators=(',', ':'), ensure_ascii=False) if body else ''
    salt, timestamp, signature = update_timestamp_salt_sig(http_method=http_method, path=path, body=str_body)
    return str_body.encode('utf-8'), salt, timestamp, signature


"""
Creates the body and headers with the right signature according to the current request contexts,
for use in the make_request method.
"""


def create_headers(http_method, url, body=None):
    body, salt, timestamp, signature = pre_call(http_method=http_method, path=url, body=body)
    return body, current_sig_headers(salt, timestamp, signature)


"""
Reusable utility method.
Use this from anywhere in your application.
"""


def make_request(method, path, body=''):
    body, headers = create_headers(method, base_url + path, body)

    if method == 'get':
        response = requests.get(base_url + path, headers=headers)
    elif method == 'put':
        response = requests.put(base_url + path, data=body, headers=headers)
    elif method == 'delete':
        response = requests.delete(base_url + path, data=body, headers=headers)
    else:
        response = requests.post(base_url + path, data=body, headers=headers)

    if response.status_code != 200:
        raise TypeError(response, method, base_url + path)
    return json.loads(response.content)
Enter fullscreen mode Exit fullscreen mode

Testing the Application

After all the three files are in place, use your CLI tool of choice to run pip install -r requirements.txt from the root of the application folder to install the dependency the Python app relies on.

Then run python app.py or python3 app.py to start the app at http://127.0.0.1:5000/ and access the same URL from your preferred browser.

Server and Python app running in the browser

From here, you can click the Reserve your slot button to submit the payment form and initiate the payment creation request, which on success should redirect you to the hosted checkout page you designed earlier.

Depending on the actions the user takes from there, they'll be redirected either to the complete_checkout_url or cancel_checkout_url you defined when creating the payment object. It's also on this checkout page that a user can select their preferred payment option according to your previous settings/allowed options.

Testing/simulating a successful checkout process

To simulate a successful transaction in the sandbox environment, use the card number 4111111111111111, a CVV code and cardholder name of any value, and any future date for the expiration date. To test all the edge cases/responses a customer may get during payment, check out this post on test credit and debit card numbers.

Beyond the Basics

From this workflow, when a customer is redirected after their actions on the checkout page, there is no payment ID to associate with the payment. Similarly, if a payment is completed at a later time (like in a recurring subscription), it won't require your customer to follow the workflow. One flexible solution to these issues is to use webhooks.

Webhooks are a great way to let your application know when something happens externally. Unlike API endpoints, you don't call webhooks—they "call you back." Rapyd includes this functionality/feature as well. You can provide a URL from your app where Rapyd sends related events, and you can then use these details to add the missing pieces from your previous workflow, such as updating completed/refunded payments in relation to the payment object created by your payment form implementation.

You already have a webhook endpoint defined in the app.py file. To start testing and taking things further, you'd need to include ngrok and expose your Python app by running ngrok http 5000 in another terminal. Then you can let Rapyd know you have a webhook URL ready to consume their requests using the Webhooks tab under the Developers section of your Rapyd dashboard:

Webhook configuration dashboard

The following image shows the payload of the incoming webhook events being printed on the terminal your Python app is running on:

Webhook events payloads

And here's a GIF showing the webhooks working in action:

Webhook events vs. user actions

Conclusion

In this tutorial, you explored the basics of how to integrate the Rapyd Collect API into a Python application to allow customers to check out from anywhere in the world using their preferred payment options. If you want a different checkout experience for your users, you can also consider Rapyd's embedded checkout option, where users can securely check out without leaving your website.

Rapyd also allows you to create the same services they offer on top of their APIs as long as you and your partner(s) are eligible. Rapyd handles all the sensitive information, allowing you to access only the metadata your app requires. You can then resell your services built on the Rapyd API to other consumers for a profit.

To learn more about Rapyd, explore the docs or sign up for a free account today.

You can find the complete code for this tutorial in this GitHub repo.

Top comments (0)