# Building a Secure Payment Gateway for Forex CFD Platforms

**URL:** <https://community.rapyd.net/t/building-a-secure-payment-gateway-for-forex-cfd-platforms/59865>\
**Category:** 👓 Blog\
**Tags:** tech-blog\
**Created:** [August 12, 2025, 7:00am UTC](https://community.rapyd.net/t/building-a-secure-payment-gateway-for-forex-cfd-platforms/59865 "2025-08-12T07:00:40Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![Community\_Team](https://avatars.discourse-cdn.com/v4/letter/c/46a35a/32.png) [@Community\_Team](https://community.rapyd.net/u/Community_Team)\
**Post date:** [August 12, 2025, 7:00am UTC](https://community.rapyd.net/t/building-a-secure-payment-gateway-for-forex-cfd-platforms/59865/1 "2025-08-12T07:00:40Z")

</div>

![Secure Forex CFD Payments Build with Rapyd API](https://us1.discourse-cdn.com/flex016/uploads/rapyd/original/3X/a/6/a665249570918fd94245b75f946247282eb0a823.png)

By: **Adeyinka Adegbenro**

A payment gateway is a service that merchants use to process customer payments, including physical point-of-sale (POS) machines and online portals. It acts as an intermediary between the customer, business, and payment processors, encrypting and transmitting payment details. Unlike a payment processor, which handles the actual movement of money, a gateway focuses on securely collecting and forwarding payment information.

🧪 Want to skip ahead? [**Get the full code on GitHub**](https://github.com/Rapyd-Samples/rapyd_forex_cfd_tutorial)

A forex contract for difference (CFD) platform allows users to speculate on the price movements of currency pairs and other financial instruments using leverage, without owning the underlying asset. These platforms facilitate frequent, high-volume money movements and charge fees for spreads (the difference between buy and sell prices), trade commissions, and overnight financing. Traders also receive payouts when they earn gains.

Given the volume and complexity of transactions, CFD platforms need a secure, scalable payment gateway that enables fast, secure deposits and multiple withdrawals; supports multiple currencies; and ensures compliance with regulations like [Cyprus Securities and Exchange Commission (CySEC)](https://www.cysec.gov.cy/en-gb/home/), [Know Your Customer (KYC)](https://en.wikipedia.org/wiki/Know_your_customer), [Payment Card Industry Data Security Standard (PCI DSS)](https://www.pcisecuritystandards.org/), and anti-money laundering (AML). Top-tier gateways, like [Rapyd](https://www.rapyd.net/), also offer fraud protection, identity verification, and high-volume scalability.

In this article, you’ll learn how to integrate real-time payments into forex CFD platforms using the Rapyd API.

## How to Implement a Secure Payment Gateway

Before you begin this tutorial, make sure [Python](https://www.python.org/downloads/) and [curl](https://curl.se/) are installed on your computer.

Then, create a new directory and a Python virtual environment:

```sh
mkdir forex_cfd && cd forex_cfdhttps://www.cysec.gov.cy/en-GB/home/

pip install virtualenv

virtualenv venv

```

Don’t forget to activate the virtual environment:

```sh
source venv/bin/activate

```

If you don’t already have one, create a free [Rapyd account](https://www.rapyd.net/). On the sign-up page, be sure to check the **I am a developer** box. Once you’re signed in, you’ll be in the default sandbox mode. From the sidebar, navigate to **Developers \> API access control** and note your sandbox credentials:

 ![Rapyd credentials](https://us1.discourse-cdn.com/flex016/uploads/rapyd/original/3X/5/1/51cbddbcb24898e1e1e1c89f1a7fef7351afc5fb.png)

Create a `.env` file:

```sh
touch .env

```

And add the following:

```auto
RAPYD_ACCESS_KEY=<YOUR_RAPYD_ACCESS_KEY>
RAPYD_SECRET_KEY=<YOUR_RAPYD_SECRET_KEY>
RAPYD_BASE_URL=https://sandboxapi.rapyd.net

```

Replace `<YOUR_RAPYD_ACCESS_KEY>` and `<YOUR_RAPYD_SECRET_KEY>` with your access key and secret key.

Then, install some dependencies:

```sh
pip install flask requests dotenv

```

[Flask](https://flask.palletsprojects.com/) serves as the API server, [Requests](https://requests.readthedocs.io/en/latest/) sends network requests to other servers, and [python-dotenv](https://pypi.org/project/python-dotenv/) loads the values in `.env` into the runtime as environmental variables.

Finally, create the application file:

```sh
touch app.py

```

### Set Up a Basic Trading Platform API

In this section, you’ll set up a basic mini trading platform backend that integrates with a payment gateway.

In your `app.py`, add the following to set up the Flask app, a mock authentication function, and functions for calling Rapyd:

```py
from flask import Flask, request, jsonify
import uuid
import requests
import hashlib
import hmac
import base64
import time
import json
import os
import random
import string
from dotenv import load_dotenv

load_dotenv() # Load environment variables from .env

app = Flask( __name__ )

# In-memory storage for users
users = {}

# Rapyd API credentials from .env
RAPYD_ACCESS_KEY = os.getenv("RAPYD_ACCESS_KEY")
RAPYD_SECRET_KEY = os.getenv("RAPYD_SECRET_KEY")
RAPYD_BASE_URL = os.getenv("RAPYD_BASE_URL")

def call_rapyd(method, path, body):
    timestamp, signature, salt, body_str = generate_rapyd_signature(method, path, body)
    
    headers = {
        "access_key": RAPYD_ACCESS_KEY,
        "salt": salt,
        "timestamp": timestamp,
        "signature": signature,
        "idempotency": str(uuid.uuid4()),
        "Content-Type": "application/json"
    }
    url = RAPYD_BASE_URL + path
    response = requests.request(method.upper(), url, data=body_str.encode('utf-8'), headers=headers)
    return response

def generate_rapyd_signature(method, path, body):
    """Generates an HMAC signature for Rapyd API requests."""
    timestamp = str(int(time.time()))
    salt = ''.join(random.choices(string.ascii_letters + string.digits, k=12))
    body_str = json.dumps(body, separators=(",", ":"), ensure_ascii=False) if body else ""
    print('concat', method , path , salt , timestamp , RAPYD_ACCESS_KEY , body_str, RAPYD_SECRET_KEY)
    to_sign = method.lower() + path + salt + timestamp + RAPYD_ACCESS_KEY + RAPYD_SECRET_KEY + body_str
    hash = hmac.new(RAPYD_SECRET_KEY.encode('utf-8'), to_sign.encode('utf-8'), hashlib.sha256)
    signature = base64.urlsafe_b64encode(str.encode(hash.hexdigest()))
    return timestamp, signature, salt, body_str

```

This code defines two functions: `generate_rapyd_signature`, which generates a hash-based message authentication code (HMAC) [signature](https://docs.rapyd.net/en/request-signatures.html) required in every request header to Rapyd, and `call_rapyd`, which is a function that sends requests to Rapyd.

Run the following code to start your Flask application:

```sh
flask run --port 8000 --reload

```

In the payment flow, the initial step is to register a new trader. To do so, append the following code to `app.py` and save it:

```py
@app.route("/register", methods=["POST"])
def register():
    """Registers a new trader and creates a Rapyd wallet."""
    data = request.json
    user_id = str(uuid.uuid4())
    first_name = data.get("first_name")
    last_name = data.get("last_name")
    country = data.get("country")
    email = data.get("email")
    phone_number = data.get('phone_number')
    wallet_id = create_wallet(first_name, last_name, country, email, phone_number)
    users[user_id] = {"first_name": data.get("first_name"), "wallet_id": wallet_id}
    return jsonify({"user_id": user_id, "wallet_id": wallet_id, "balance": 0, "message": "User registered successfully."})

def create_wallet(first_name, last_name, country, email, phone_number):
    """Creates a Rapyd eWallet for the trader."""
    wallet_data = {
        "type": "person",
        "first_name": first_name,
        "last_name": last_name,
        "ewallet_reference_id": str(uuid.uuid4()),
        "contact": {
            "phone_number": phone_number,
            "email": email,
            "first_name": first_name,
            "last_name": last_name,
            "country": country,
        }
    }
    
    path = "/v1/ewallets"
    response = call_rapyd('post', path, wallet_data)
    if response.status_code == 200:
        data = response.json()
        print('data======>', data)
        return data['data']['id']
    else:
        print(response.json())
    return None

```

Calling the `/register` endpoint with the `first_name`, `last_name`, `country`, `email`, and `phone number` creates a new user account. Once the trader account has been successfully created, a call is made to the Rapyd endpoint `/v1/ewallets` to create a wallet for the trader. The trader can fund and withdraw from this wallet. The platform can also deposit gains into it and charge fees from it.

Next, in your app directory, create a new file called `body.json`:

```sh
touch body.json

```

`body.json` holds the current payload for any `POST` requests you make to any endpoints you’re testing. This helps you escape some formatting issues that may occur on the terminal while using curl. In `body.json`, add the following:

```json
{
    "first_name": "Jane",
    "last_name": "Doe",
    "country": "SG",
    "email": "jdoe@gmail.com",
    "phone_number": "5554433222"
}

```

Then, in a new terminal, call the `/register` endpoint:

```sh
$ curl -H "Content-Type: application/json" --request POST --data @body.json http://localhost:8000/register

{"balance":0,"message":"User registered successfully.","user_id":"5329af81-5afc-4d16-b868-d210d53bcb8d","wallet_id":"ewallet_7b4034daacfc588a3054c846f66c2336"}

```

This creates a trader in the in-memory `users` storage and a Rapyd wallet. The request returns the `user_id` and `wallet_id`. Take note of your `wallet_id` because it’s used in subsequent requests.

From the Rapyd Client Portal, click on **Wallets** to see the newly created trader wallet:

 ![Image of the trader's new wallet created, shown on the Rapyd Client Portal](https://us1.discourse-cdn.com/flex016/uploads/rapyd/original/3X/3/1/3141cf15c68fee32cf0b6430adb370b552f2981d.png)

### Create a Payment Collection Endpoint

Once you’ve created the trader’s account, the next step is for the platform to allow the trader to initiate a forex CFD trade. To see how to collect funds from the trader, you need to add some code that helps the platform receive money from the trader in any currency.

Append the following into `app.py` and save it:

```py
@app.route("/deposit", methods=["POST"])
def deposit():
    """Handles deposits using Rapyd's Collect API."""
    data = request.json
    amount = data.get("amount")
    currency = data.get("currency", "USD")
    country = data.get("country", "US")
    fund_trader_wallet = data.get("fund_trader_wallet")
    
    # Create a payment checkout page
    # By default, the money goes into the trading platform's wallet
    payment_data = {
        "amount": amount,
        "currency": currency,
        "country": country,
        "complete_payment_url": "http://localhost:8000/payment_success",
        "error_payment_url": "http://localhost:8000/payment_failure"
    }
    # This option causes the money to go into the trader's own wallet
    if fund_trader_wallet:
        payment_data.update({
            "ewallet": data.get("wallet_id"),
        })
    
    path = "/v1/checkout"
    response = call_rapyd("post", path, payment_data)

    if response.status_code == 200:
        payment_response = response.json()
        return jsonify({
            "message": "Deposit initiated",
            "checkout_url": payment_response["data"]["redirect_url"]
        })
    else:
        return jsonify(response.json()), response.status_code

@app.route("/payment_success", methods=["GET"])
def payment_success():
    return jsonify({"message": "Payment Succeeded."})

@app.route("/payment_failure", methods=["GET"])
def payment_failure():
    return jsonify({"message": "Payment Failed."})

```

The `/deposit` endpoint calls the Rapyd `/v1/checkout` endpoint to create a payment link where the trader can be redirected to within a user interface.

The money can go in two directions, depending on the payload sent:

1. The platform’s Rapyd wallet, which is created by default when a Rapyd account is created.
2. The trader’s Rapyd wallet.

#### Collect Directly on the Platform

To test receiving a payment from the trader into the platform’s Rapyd wallet, start by updating your `body.json`:

```json
{
    "amount": "7000",
    "currency": "SGD",
    "country": "SG"
}

```

Then, test the endpoint:

```sh
$ curl -H "Content-Type: application/json" --request POST --data @body.json http://localhost:8000/deposit

{"checkout_url":"https://sandboxcheckout.rapyd.net/?token=checkout_1e1ef6dac20fe770d51b9356d0585ace","message":"Deposit initiated"}

```

If the response is returned to a frontend app, redirect the trader to the `checkout_url`. Then, use [test cards](https://docs.rapyd.net/en/card-numbers-for-testing.html) to simulate a sample card payment:

 ![The Checkout page](https://us1.discourse-cdn.com/flex016/uploads/rapyd/original/3X/2/d/2d1b11309a7e06622de85fc7640286691afcbf16.png)

Manually visit the `checkout_url` you received in the browser’s output. Once the user has paid through the link, the funds are sent directly to the trading platform’s wallet.

To see the payment reflected in the platform’s wallet on the Rapyd Client Portal, click **My account \> Account Balance**.

#### Fund the Trader’s Rapyd Wallet

To fund the trader’s Rapyd wallet, you need to send a request to the `/deposit` endpoint (similar to the previous request). However, this time, the `fund_trader_wallet` is set to `true`, and the `wallet_id` is provided:

```json
{
    "wallet_id": "<wallet_id>",
    "amount": "1000",
    "currency": "SGD",
    "country": "SG",
    "fund_trader_wallet": "true"
}

```

Make sure you replace `<wallet_id>` with the wallet ID of the trader account you created in the last section. Then, call the `/deposit` endpoint again:

```sh
$ curl -H "Content-Type: application/json" --request POST --data @body.json http://localhost:8000/deposit

{"checkout_url":"https://sandboxcheckout.rapyd.net/?token=checkout_d1bdf507258a33ad1e3575a0403d7c5c","message":"Deposit initiated"}

```

Once the trader completes the payment using the provided link, the changes are reflected in their personal wallet. From the Rapyd Client Portal sidebar, click **Wallets** to see the trader Jane Doe’s wallet balance. You should now see a balance of $1,000 SGD:

 ![Trader's Rapyd wallet balance](https://us1.discourse-cdn.com/flex016/uploads/rapyd/original/3X/9/b/9bdb47a3ae1265a4f085b8e2869989b4ee0c00a5.png)

#### Debit the Trader’s Rapyd Wallet

Now that the trader Jane Doe has a balance of $1,000 SGD, you can try debiting from the trader’s wallet. Append the following code to `app.py` and save it:

```py
@app.route("/debit_trader_wallet", methods=["POST"])
def debit_trader_wallet():
    """Debit trader's Rapyd Wallet."""
    data = request.json
    amount = data.get("amount")
    currency = data.get("currency", "USD")

    wallet_id = data.get("wallet_id")
    if not wallet_id:
        return jsonify({"error": "User does not have a wallet"}), 400
    
    wallet_data = {
        "ewallet": wallet_id,
        "amount": amount,
        "currency": currency
    }
    
    path = "/v1/account/withdraw"
    response = call_rapyd('post', path, wallet_data)
    
    if response.status_code == 200:
        deposit_response = response.json()
        return jsonify({"message": "Funds removed from wallet", "transaction": deposit_response["data"]})
    else:
        return jsonify(response.json()), response.status_code

```

This code calls the Rapyd `/v1/account/withdraw` to withdraw the specified amount and currency from the trader’s wallet.

Update your `body.json` with the payload necessary for the request:

```json
{
    "wallet_id": "<wallet_id>",
    "amount": "100",
    "currency": "SGD"
}

```

The payload signifies that $100 SGD should be withdrawn from the trader’s wallet. To test this, run the following:

```sh
$ curl -H "Content-Type: application/json" --request POST --data @body.json http://localhost:8000/debit_trader_wallet

{"message":"Funds removed from wallet","transaction":{"account_id":"2a07049a-9f59-4492-b49c-00a60d6c860d","amount":-100,"balance":900,"balance_type":"available_balance","currency":"SGD","id":"9487b395-4480-4c68-b9e6-16d6bf2fdda1","metadata":{},"phone_number":null}}

```

From the Rapyd Client Portal sidebar, navigate to **Wallets \> Transactions**. You should see the last outbound transaction of $100 SGD:

 ![Trader's wallet balance](https://us1.discourse-cdn.com/flex016/uploads/rapyd/original/3X/0/e/0ea83f33c9a74f3bb8bb3415f5d80e607dc06d55.png)

### Disburse Payments

In the following section, you’ll learn how to handle payouts to the trader in the form of gains, refunds, and withdrawals.

#### Credit the Trader’s Rapyd Wallet

Append this piece of code to your `app.py` and save it:

```py
@app.route("/credit_trader_wallet", methods=["POST"])
def credit_trader_wallet():
    """credit Trader's Rapyd Wallet."""
    data = request.json
    amount = data.get("amount")
    currency = data.get("currency", "USD")

    wallet_id = data.get("wallet_id")
    if not wallet_id:
        return jsonify({"error": "User does not have a wallet"}), 400
    
    wallet_data = {
        "ewallet": wallet_id,
        "amount": amount,
        "currency": currency
    }
    
    path = "/v1/account/deposit"
    response = call_rapyd('post', path, wallet_data)
    
    if response.status_code == 200:
        deposit_response = response.json()
        return jsonify({"message": "Funds added to trader's wallet", "transaction": deposit_response["data"]})
    else:
        return jsonify(response.json()), response.status_code

```

In this code, the Flask endpoint `/credit_trader_wallet` calls the Rapyd `/v1/account/deposit` endpoint. In the payload to Rapyd, notice how the trader’s wallet was added alongside the amount and currency.

Update `body.json` with the following:

```json
{
    "wallet_id": "<wallet_id>",
    "amount": "600",
    "currency": "SGD"
}

```

Replace `<wallet_id>` with the trader’s wallet ID created in the previous section. This credits the trader’s wallet with $600 SGD. Run the following to test this:

```sh
$ curl -H "Content-Type: application/json" --request POST --data @body.json http://localhost:8000/credit_trader_wallet

{"message":"Funds added to trader's wallet","transaction":{"account_id":"2a07049a-9f59-4492-b49c-00a60d6c860d","amount":600,"balance":1500,"balance_type":"available_balance","currency":"SGD","id":"e01f5392-6812-4a08-94f5-844ebbabd6f3","metadata":{},"phone_number":null}}

```

 ![Screenshot of wallet transaction](https://us1.discourse-cdn.com/flex016/uploads/rapyd/original/3X/a/c/acc2bcdc6f7fa4142dee6ff8e06b80b41a5c9719.png)

#### Credit the Trader’s Non-Rapyd Accounts

To credit a trader’s external account (_eg_ a bank account), you need to query Rapyd for the required fields based on the payout currency and country.

There are two pieces of data to retrieve:

1. The **payment method type** compatible with the selected payout currency and country.
2. The **required fields** that must be provided to complete the payout process successfully.

This has been consolidated into two endpoints. Each endpoint calls Rapyd endpoints `/v1/payout_method_types` and `/v1/payout_methods/{payout_type}/required_fields`, respectively, using the `GET` method. See the [Rapyd Payout Docs](https://docs.rapyd.net/en/payout.html) for more details on this topic.

Append the following code block to your `app.py`:

```py
@app.route("/get_payout_types", methods=["GET"])
def get_payout_types():
    
    data = request.args
    payout_params = {
        "beneficiary_country": data.get("beneficiary_country"),
        'beneficiary_entity_type': data.get("entity_type"),
        "payout_currency": data.get("payout_currency"),
        "category": data.get("category")
    }

    path = '/v1/payout_method_types?' + '&'.join(list(f'{param}={value}' for param, value in payout_params.items()))

    response = call_rapyd('get', path, None)
    if response.status_code == 200:
        return jsonify(response.json())
    else:
        return jsonify(response.json()), response.status_code
    
@app.route("/get_payout_required_fields", methods=["GET"])
def get_payout_required_fields():

    data = request.args
    payout_type = data.get('payout_method_type')
    payout_params = {
        "beneficiary_country": data.get("beneficiary_country"),
        'beneficiary_entity_type': data.get("beneficiary_entity_type"),
        "payout_currency": data.get("payout_currency"),
        "payout_amount": data.get("payout_amount"),
        "payout_method_type": data.get("payout_method_type"),
        "sender_currency": data.get("sender_currency"),
        "sender_country": data.get("sender_country"),
        "sender_entity_type": data.get("sender_entity_type")
    }
    params = '&'.join(list(f'{param}={value}' for param, value in payout_params.items()))
    path = f'/v1/payout_methods/{payout_type}/required_fields?{params}'

    response = call_rapyd('get', path, None)
    if response.status_code == 200:
        return jsonify(response.json())
    else:
        return jsonify(response.json()), response.status_code

```

Run the following command to test the `get_payout_types` endpoint:

```sh
$ curl 'http://localhost:8000/get_payout_types?beneficiary_country=SG&beneficiary_entity_type=individual&payout_currency=SGD&category=bank&sender_currency=SGD'

{
    "data": [
        {
            "amount_range_per_currency": [
                {
                    "maximum_amount": null,
                    "minimum_amount": null,
                    "payout_currency": "SGD"
                }
            ],
            "beneficiary_country": "sg",
            "beneficiary_entity_types": [
                "company",
                "individual"
            ],
            "category": "bank",
            "estimated_time_of_arrival": "An estimated receipt time is not yet available for this payout method.",
            "image": "/checkout/sg_general_bank.png",
            "is_cancelable": 0,
            "is_expirable": 0,
            "is_location_specific": 0,
            "maximum_expiration_seconds": null,
            "minimum_expiration_seconds": null,
            "name": "Bank Transfer to Singapore",
            "payout_currencies": [
                "SGD"
            ],
            "payout_method_type": "sg_general_bank",
            "sender_country": "*",
            "sender_currencies": [
                "SGD"...
            ],
            "sender_entity_types": [
                "company",
                "individual"
            ],
            "status": 1
        }
    ],
    "status": {
        "error_code": "",
        "message": "",
        "operation_id": "ecc2c24c-f5f9-4947-97a6-96228f8c3c4a",
        "response_code": "",
        "status": "SUCCESS"
    }
}

```

The output from `/get_payout_types` lets you know that the appropriate `payout_method_type` for sending money out to Singapore in SGD is `sg_general_bank`. The next query retrieves the required fields needed to make a payout to Singapore (SG).

Run the following command to test the `/get_payout_required_fields` endpoint:

```sh
$ curl 'http://localhost:8000/get_payout_required_fields?beneficiary_country=SG&beneficiary_entity_type=Individual&payout_currency=SGD&payout_amount=200&payout_method_type=sg_general_bank&sender_currency=SGD&sender_country=SG&sender_entity_type=individual'

{
    "data": {
        "batch_file_header": "payout_method_type,sender_currency,payout_currency,beneficiary.payment_type,beneficiary.first_name,beneficiary.last_name,beneficiary.address,beneficiary.city,beneficiary.country,beneficiary.account_number,beneficiary.bic_swift,sender.country,sender.city,sender.address,sender.first_name,sender.last_name,sender.date_of_birth",
        "beneficiary_country": "sg",
        "beneficiary_entity_type": "Individual",
        "beneficiary_required_fields": [
            {
                "is_required": true,
                "name": "payment_type",
                "regex": "regular"
            },
            {
                "is_required": true,
                "name": "first_name",
                "regex": "^([^0-9]{1,255})$"
            }...
                    ],
        "sender_required_fields": [
            {
                "is_required": true,
                "name": "country",
                "regex": "^[A-z]{2}"
            },
            {
                "is_required": true,
                "name": "city",
                "regex": "^.{1,255}"
            }....
        ],
        "status": 1
    },
    "status": {
        "error_code": "",
        "message": "",
        "operation_id": "8677fef9-933b-4efb-8635-ebc40d5f6bbc",
        "response_code": "",
        "status": "SUCCESS"
    }
}

```

The most important part of this response is the `beneficiary_required_fields` and the `sender_required_fields`. Their values tell you which fields are required when trying to send a payout request.

Next, send a payout, making sure that the required fields for both the beneficiary and sender are present. Append the following code to `app.py` and save it:

```py

@app.route("/payout", methods=["POST"])
def payout():
    """Initiates a payout to a beneficiary."""
    data = request.json
    amount = data.get("amount")
    currency = data.get("currency", "USD")

    wallet_id = data.get("wallet_id")
    if not wallet_id:
        return jsonify({"error": "User does not have a wallet"}), 400
    
    payout_data = {
        "beneficiary": data.get("beneficiary"),
        "beneficiary_country": data.get("beneficiary_country"),
        "beneficiary_entity_type": data.get("beneficiary_entity_type"),
        "description": data.get("description"),
        "ewallet": wallet_id,
        "payout_amount": data.get("payout_amount"),
        "payout_currency": data.get("payout_currency"),
        "payout_method_type": data.get("payout_method_type"),
        "sender": data.get("sender"),
        "sender_country": data.get("sender_country"),
        "sender_currency": data.get("sender_currency"),
        "sender_entity_type": data.get("sender_entity_type")
    }
    
    path = "/v1/payouts"
    response = call_rapyd('post', path, payout_data)
    
    if response.status_code == 200:
        return jsonify(response.json())
    else:
        return jsonify(response.json()), response.status_code

```

Now, update your `body.json` with the necessary fields:

```json
{
    "wallet_id": "wallet_id",
    "beneficiary": {
        "payment_type": "regular",
        "first_name": "Jane",
        "last_name": "Doe",
        "address": "1 Main Street",
        "city": "Anycity",
        "country": "SG",
        "account_number": "BG96611020345678",
        "bic_swift": "ABCDSGGG",
        "currency": "SGD",
        "category": "bank",
        "entity_type": "individual"
    },
    "beneficiary_entity_type": "individual",
    "payout_amount": 200,
    "payout_currency": "SGD",
    "payout_method_type": "sg_general_bank",
    "sender": {
        "country": "SG",
        "city": "Anycity",
        "address": "123 First Street",
        "first_name": "Jane",
        "last_name": "Doe",
        "state": "AnyState",
        "date_of_birth": "22/02/1980"
    },
    "sender_country": "SG",
    "sender_currency": "SGD",
    "sender_entity_type": "individual",
    "description": "Payout - Bank Transfer: From Rapyd trader wallet"
}

```

Don’t forget to replace `wallet_id` with your trader’s wallet ID. This should pay out $200 SGD to the trader’s bank account. To test it, run the following:

```sh
$ curl -H "Content-Type: application/json" --request POST --data @body.json http://localhost:8000/payout

{"data":{"amount":200,"batch_file":null,"beneficiary":{"account_number":"BG96611020345678","address":"1 Main Street","bic_swift":"ABCDSGGG","category":"bank","city":"Anycity","country":"SG","currency":"SGD","entity_type":"individual","first_name":"Jane","id":"beneficiary_33b2753c145b64e11b1784db192188c6","last_name":"Doe","name":"Jane Doe","payment_type":"regular"},"beneficiary_country":"SG","created_at":1744024524,"crypto_payout_hash":null,"description":"Payout - Bank Transfer: From Rapyd trader wallet","error":null,"estimated_time_of_arrival":"An estimated receipt time is not yet available for this payout method.","ewallets":[{"amount":200,"ewallet_id":"ewallet_7b4034daacfc588a3054c846f66c2336","percent":100}],"expiration":null,"fx_rate":1,"gc_error_code":null,"id":"payout_7e05077d32654ad5140fbd377bb15e3b","identifier_type":null,"identifier_value":null,"instructions":[{"name":"instructions","steps":[{"step1":"The funds will be transferred to the provided account details of the beneficiary ."}]}],"metadata":{},"paid_amount":0,"paid_at":null,"payout_currency":"SGD","payout_fees":null,"payout_method_type":"sg_general_bank","payout_type":"bank","sender":{"address":"123 First Street","city":"Anycity","country":"SG","currency":"SGD","date_of_birth":"22/02/1980","entity_type":"individual","first_name":"Jane","id":"sender_469debc0b06c86d8cd7fa9bb99a5947c","last_name":"Doe","name":"Jane Doe","state":"AnyState"},"sender_amount":200,"sender_country":"SG","sender_currency":"SGD","statement_descriptor":null,"status":"Created"},"status":{"error_code":"","message":"","operation_id":"c1ea127f-d958-40b7-aa2e-c7f3540b7d57","response_code":"","status":"SUCCESS"}}

```

From the Rapyd Client Portal, click **Wallets \> Transactions** to see your latest transactions. You should see that $200 SGD was sent from the trader Jane Doe’s wallet, leaving a balance of $1,300 SGD:

 ![Payment disbursement of $200 SGD from Jane Doe's wallet to a bank account](https://us1.discourse-cdn.com/flex016/uploads/rapyd/original/3X/3/7/3754c2c5867fb05cb8a53ab6075ebfe2d2227727.png)

All the code for this tutorial is available in this [GitHub repo](https://github.com/Rapyd-Samples/rapyd_forex_cfd_tutorial).

## Conclusion

Trading platforms require security, accuracy, and speed to handle payments at scale. Payment gateways help make the user experience as seamless as possible, but you need to make sure you use a solution that offers fast, secure deposits; supports multiple currencies; and ensures compliance and scalability.

In this tutorial, you learned how to integrate real-time payments into a forex CFD trading platform using the [Rapyd API](https://docs.rapyd.net/en/api-reference.html).

Rapyd is a global platform that simplifies financial operations, helping businesses integrate payments, payouts, fraud protection, and compliance with ease. It eliminates the complexity of managing multiple payment systems across borders, currencies, and regulations. Start building with [Rapyd](https://www.rapyd.net/) today.
