Introduction
SPINPAY is a simple, fast and reliable payment engine with open architecture. Founded back in 2018 now it is supported and constantly developing by the community of software engineers with bold background in payment and e-wallet systems.
SPINPAY Business API - a solution specifically designed for internet businesses in need of multicurrency payment processing. We support all major currencies.
Environments
There are two environments available for integration:
- Production environment: https://business.cixsdpxj.info
Sandbox Environment
Sandbox provides full functionality but it only emulates processing, no actual bank transactions are made. You can use the following PAN for tests:
- 4617611794313933: CONFIRMED as 3-D Secure transaction
- 4626233193837898: DECLINED as 3-D Secure transaction
- 4392963203551251: CONFIRMED as non 3-D Secure transaction
- 4730198364688516: DECLINED as non 3-D Secure transaction
- 4627342642639018: APPROVED PAYOUT
- 4968357931420422: DECLINED PAYOUT
You can use any cardholder name, expiry date and CVV2/CVC2 with these PANs. 3-D Secure is also emulated with a page that doesn’t require any password but only shows you 2 buttons. One button is for successful authentication, another is for failed authentication. Note, that when you chose to fail authentication, order is always declined, no matter what PAN was used.
Production Environment
Once you complete integration with Sandbox environment you will be provided with Production credentials. These are completely different credentials, not related with the ones on Sandbox. Production always makes real bank transactions, cards from Sandbox are not supported on this environment.
Authentication
curl https://business.cixsdpxj.info/v1/charges \
-H "Authorization: Bearer merchant_private_key"
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_HTTPHEADER => array(
"authorization: Bearer merchant_private_key",
),
));
import http.client
conn = http.client.HTTPSConnection("...")
headers = {
'authorization': "Bearer merchant_private_key",
}
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://business.cixsdpxj.info/api/v1/payouts")
...
.addHeader("content-type", "application/json")
.addHeader("authorization", "Bearer merchant_private_key")
.build();
Response response = client.newCall(request).execute();
Authenticate your account when using the API, by including your secret API key which has been sent via email during registration. Management of your API keys can be done within the Backoffice. Your API keys carry importance and privileges, be sure to store them securely. Please do not share your secret API keys in publicly accessible areas such GitHub and client-side code areas.
Authentication to the API is performed via bearer auth keys (for cross-origin requests), use -H “Authorization: Bearer merchant_private_key”.
All API requests must be made through HTTPS. Calls made through plain HTTP will fail. API requests without authentication will also fail.
Payments
SPINPAY payment processing REST API.
Create
Code: Copy
curl "https://business.cixsdpxj.info/api/v1/payments" \
-X POST \
-H "Authorization: Bearer merchant_private_key" \
-H "Content-Type: application/json" -d '{
"product": "Your Product",
"amount": 10020,
"currency": "EUR",
"redirectSuccessUrl": "https://your-site.com/success",
"redirectFailUrl": "https://your-site.com/fail",
"locale": "en",
"orderNumber": "your order number",
"extraReturnParam": "your order id or other info",
"customer": {
"email": "yourmail@gmail.com"
}
}'
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://business.cixsdpxj.info/api/v1/payments",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "{ \"product\" : \"Your Product\", \"amount\" : "10000", \"currency\" : \"CNY\", \"redirectSuccessUrl\" : \"https://your-site.com/success\", \"redirectFailUrl\" : \"https://your-site.com/fail\", \"extraReturnParam\" : \"your order id or other info\", \"orderNumber\" : \"your order number\", \"locale\" : \"zh\"\n}",
CURLOPT_HTTPHEADER => array(
"authorization: Bearer merchant_private_key",
"content-type: application/json"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
from django.http import HttpResponseRedirect, HttpResponse
import requests
import json
def pay(request) :
MERCHANT_PRIVATE_KEY = 'merchant_private_key'
LIVE_URL = 'https://business.cixsdpxj.info';
SANDBOX_URL = 'https://business.cixsdpxj.info'
payload = {
"product" : request.POST['product_name'],
"amount" : request.POST['order_amount'],
"currency" : "CNY",
"redirectSuccessUrl": request.POST['notify_url'],
"redirectFailUrl" : request.POST['return_url'],
"extraReturnParam" : request.POST['order_no'],
"orderNumber" : request.POST['order_number'],
"locale" : request.POST['locale']
}
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer %s' % (MERCHANT_PRIVATE_KEY)
}
resp = requests.post('%s/api/v1/payments' % (SANDBOX_URL), json=payload, headers=headers)
if resp.status_code == 200:
resp_payload = json.loads(resp.text)
return HttpResponseRedirect(resp_payload['processingUrl'])
else:
return HttpResponse('<html><body><span>Something gone wrong: %s</span></body></html>' % (resp.status_code))
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("product", "SPINPAY Example Payment");
params.put("amount", "1000");
params.put("currency", "EUR");
params.put("redirectSuccessUrl", "[sucess redirect url]");
params.put("redirectFailUrl", "[fail redirect url]");
params.put("orderNumber", "[merchat system order number]");
params.put("extraReturnParam", "[some additional params]");
params.put("locale", "[user locale]");
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://business.cixsdpxj.info/api/v1/payments")
.post(RequestBody.create(JSON, new Gson().toJson(params)))
.addHeader("content-type", "application/json")
.addHeader("authorization", "Bearer merchant_private_key")
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("response ", "onFailure(): " + e.getMessage() );
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String resp = response.body().string();
Log.e("response ", "onResponse(): " + resp );
}
});
Return status 200 and JSON: Copy
{
"success": true | false,
"errors": [],
"token": "[payment token]",
"processingUrl": "https://business.cixsdpxj.info/p/[payment token]",
"payment": {
"amount": "10020",
"currency": "EUR",
"status": "init"
},
"redirectRequest": {
"url": "[redirect url, for example ACS URL for 3ds]",
"params": {
"PaReq": "[PaReq for current payment]",
"TermUrl": "https://business.cixsdpxj.info/checkout_results/[payment token]/callback_3ds"
},
"type": "post"
}
}
Initialize payments - to begin receiving payments, you must first call using the following script. This will enable you to obtain a payment token, which will be required later to complete API integration.
HTTP Request via SSL
POST '/api/v1/payments'
Query Parameters
| Parameter | Mandatory | Description | Validation |
|---|---|---|---|
| product | yes | Product name (Service description) (example: 'iPhone'). | minLength: 5, maxLength: 255 |
| amount | yes | Payment amount in cents (10020), except JPY | minLength: 1, maxLength: 32 |
| currency | yes | Currency code (CNY, EUR, USD, JPY). | minLength: 3, maxLength: 3 |
| callbackUrl | yes | The server URL a merchant will be notified about a payment finalisation | Valid URI format |
| redirectSuccessUrl | no | The URL a customer will be redirected to in the case of successfull payment | Valid URI format |
| redirectFailUrl | no | The URL a customer will be redirected to in the case of payment error or failure | Valid URI format |
| extraReturnParam | no | Bank/Payment method list, description, etc | minLength: 1, maxLength: 1024 |
| expires_at | no | Expired payment time for requests without a bank card | minLength: 1 |
| orderNumber | no | The current order number from a company system. | minLength: 3, maxLength: 255 (string) |
| locale | no | The locale is used on a payment page by default. Currently supported locales: en, zh and jp from ISO 639-1. | minLength: 2, maxLength: 5 (string) |
| walletToken | no | Set this parameter when making recurring payment from a customer’s wallet. A customer will receive notification and has to confirm the payment. | returns by API for recurring payments only |
| recurring | no | Set this parameter to true when initializing recurring payment. | boolean |
| recurringToken | no | Set this parameter when making recurring payment previously initialized with recurring param. | returns by API for recurring payments only |
| needConfirmation | no | Set this parameter whe making payment in two steps (preAuth and confirm/decline) | |
| recurring_data | no | Recurring data object for Host2Host payments. | |
| card | no | Card object for Host2Host payments. | |
| customer | no | Customer object for Host2Host payments. |
Card Object Parameters
| Parameter | Mandatory | Description | Validation |
|---|---|---|---|
| pan | yes | Customer’s card number (PAN). Any valid card number, may contain spaces | Valid card number (16-19 digits) |
| expires | yes | Customer’s card expiration date. Format: mm/yyyy | mm/yyyy format |
| holder | yes | Customer’s cardholder name. Any valid cardholder name | minLength: 5, maxLength: 50 |
| cvv | yes | Customer’s CVV2 / CVC2 / CAV2 | minLength: 3, maxLength: 3 Only digits (\d+) |
Customer Object Parameters (optional)
| Parameter | Mandatory | Description | Validation |
|---|---|---|---|
| yes | Customer’s email, is mandatory if Customer object posted on a request | Valid email format | |
| address | no | Customer's billing address | minLength: 5, maxLength: 55 |
| country | no | Customer's billing country | ISO country code format "GB" |
| city | no | Customer's billing city | minLength: 4, maxLength: 55 |
| region | no | Customer's billing region | minLength: 5, maxLength: 55 |
| postcode | no | Customer's billing ZipCode | minLength: 4, maxLength: 55 |
| phone | no | Customer's billing phone number | minLength: 6, maxLength: 20 |
| ip | no | Customer IP address | Valid IP address format (XX.XX.XX.XX) |
| browser | no | Customer browser object for 3ds2 payments. |
Customer browser object for 3ds2 payments (optional)
| Parameter | Mandatory | Description | Example |
|---|---|---|---|
| accept_header | no | Browser's content type | text/html |
| color_depth | no | Browser's color depth value | 32 |
| ip | no | Browser's ip | 177.255.255.35 |
| language | no | Browser's language | ru |
| screen_height | no | Browser's screen height | 1080 |
| screen_width | no | Browser's screen width | 1920 |
| tz | no | Browser's time zone | 180 |
| user_agent | no | Browser's user agent | Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0 |
| java_enabled | no | Is java enabled | true |
| javascript_enabled | no | Is javascript enabled | true |
| window_width | no | Browser's window width | 1920 |
| window_height | no | Browser's bilwindowling height | 1080 |
Recurring data object for payments (optional)
| Parameter | Mandatory | Description | Validation |
|---|---|---|---|
| days | no | Customer days object for payments. | Number of days between authorizations from 1 |
| exp_date | no | Customer exd_date object for payments. | Period of validity of periodic payments in format YYYYMMDD |
Payments Providers
Code: Copy
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("product", "SPINPAY Example Payment");
params.put("amount", "1000");
params.put("currency", "EUR");
params.put("redirectSuccessUrl", "[sucess redirect url]");
params.put("redirectFailUrl", "[fail redirect url]");
params.put("orderNumber", "[merchat system order number]");
params.put("extraReturnParam", "[some additional params]");
params.put("locale", "[user locale]");
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://business.cixsdpxj.info/api/v1/payments")
.post(RequestBody.create(JSON, new Gson().toJson(params)))
.addHeader("content-type", "application/json")
.addHeader("authorization", "Bearer merchant_private_key")
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("response ", "onFailure(): " + e.getMessage() );
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String resp = response.body().string();
Log.e("response ", "onResponse(): " + resp );
}
});
Return status 200 and JSON: Copy
{
"success": true | false,
"errors": [],
"token": "[payment token]",
"processingUrl": [
{
"webmoney": "http://business.cixsdpxj.info/p/165998589a413b56ae72fbfdc15b016b/webmoney?locale=en"
},
{
"bank_card": "http://business.cixsdpxj.info/p/165998589a413b56ae72fbfdc15b016b/bank_card?locale=en"
},
{
"qiwi_wallet": "http://business.cixsdpxj.info/p/165998589a413b56ae72fbfdc15b016b/qiwi_wallet?locale=en"
},
{
"skrill_wallet": "http://business.cixsdpxj.info/p/165998589a413b56ae72fbfdc15b016b/skrill_wallet?locale=en"
}
],
"selectorURL": "https://business.cixsdpxj.info/select/[payment token]/",
"payment": {
"amount": "10020",
"currency": "CNY",
"status": "init"
},
"redirectRequest": {
"url": "[redirect url, for example ACS URL for 3ds]",
"params": {
"PaReq": "[PaReq for current payment]",
"TermUrl": "https://business.cixsdpxj.info/checkout_results/[payment token]/callback_3ds"
},
"type": "post"
}
}
In case multiple payment providers enabled to a merchant account, Create payment reponse JSON will have processingUrl object represented as an array of available payment providers (please refer to JSON response). Use those URLs to redirect your customer to a payment provider (method).
List of payment providers
In case you want a customer to choose a payment provider (method) it might be convenient to use a specific page (widget) with payment provider list, which is availabe by "selectorURL" parameter in JSON response object
List
Code: Copy
curl "https://business.cixsdpxj.info/api/v1/payments?dateFrom=2016-05-11&page=1&perPage=1" \
-H "Authorization: Bearer merchant_private_key"
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://business.cixsdpxj.info/api/v1/payments?dateFrom=2016-05-11&page=1&perPage=1",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => array(
"authorization: Bearer merchant_private_key"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://business.cixsdpxj.info/api/v1/payments")
.get()
.addHeader("authorization", "Bearer merchant_private_key")
.build();
Response response = client.newCall(request).execute();
Return status 200 and JSON: Copy
{
"success": true | false,
"errors": [],
"status": 200,
"totalCount": 10,
"curentPage": 1,
"perPage": 1,
"totalPage": 10,
"payments": [
{
"id": 1,
"status": "sent",
"token": "[payment token]",
"currency": "CNY",
"product": "Your Product",
"redirect_success_url": "https://your-site.com/success",
"redirect_fail_url": "https://your-site.com/fail",
"amount": 10000,
"created_at": "2016-06-27T14:13:00.273Z",
"updated_at": "2016-06-27T14:15:44.715Z",
"extra_return_param": "your order id or other info",
"operation_type": "pay",
"order_number": 1
}
]
}
Payments List - this is the method used to display the list of returned payments.
HTTP Request via SSL
GET '/api/v1/payments'
Query Parameters
| Parameter | Description | Required |
|---|---|---|
| dateFrom | Date from (example: '2015-01-01') | No |
| dateTo | Date to (example: '2015-01-02') | No |
| page | Page number (default: 1) | No |
| perPage | Payment per page (max: 500, default: 20) | No |
| operationType | Operation type (Available values: pays, payouts, all) | No |
| orderNumber | Merchant's order number | No |
Confirm Two-Step
Code: Copy
curl "https://business.cixsdpxj.info/api/v1/payments/confirm" \
-X POST \
-H "Authorization: Bearer merchant_private_key" \
-H "Content-Type: application/json" -d '{
"token" : "Your Product"
}'
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://business.cixsdpxj.info/api/v1/payments/confirm",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "{ \"token\" : \"payment token\""\n}",
CURLOPT_HTTPHEADER => array(
"authorization: Bearer merchant_private_key",
"content-type: application/json"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
from django.http import HttpResponseRedirect, HttpResponse
import requests
import json
def pay(request) :
MERCHANT_PRIVATE_KEY = 'merchant_private_key'
LIVE_URL = 'https://business.cixsdpxj.info';
SANDBOX_URL = 'https://business.cixsdpxj.info'
payload = {
"token" : request.POST['token payment']
}
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer %s' % (MERCHANT_PRIVATE_KEY)
}
resp = requests.post('%s/api/v1/payments/confirm' % (SANDBOX_URL), json=payload, headers=headers)
if resp.status_code == 200:
resp_payload = json.loads(resp.text)
return HttpResponseRedirect(resp_payload['processingUrl'])
else:
return HttpResponse('<html><body><span>Something gone wrong: %s</span></body></html>' % (resp.status_code))
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("token", "payment token");
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://business.cixsdpxj.info/api/v1/payments/confirm")
.post(RequestBody.create(JSON, new Gson().toJson(params)))
.addHeader("content-type", "application/json")
.addHeader("authorization", "Bearer merchant_private_key")
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("response ", "onFailure(): " + e.getMessage() );
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String resp = response.body().string();
Log.e("response ", "onResponse(): " + resp );
}
});
Return status 200 and JSON: Copy
{
"success": true | false,
"result": 0,
"status": 200,
"payment": {
"amount": 100,
"gateway_amount": 100,
"currency": "USD",
"status": "approved|declined",
"two_stage_mode": true
}
}
Confirm Two-Step payment by providing a payment token.
HTTP Request via SSL
POST '/api/v1/payments/confirm'
Query Parameters
| Parameter | Mandatory | Description |
|---|---|---|
| token | yes | Payment token. |
Decline Two-Step
Code: Copy
curl "https://business.cixsdpxj.info/api/v1/payments/decline" \
-X POST \
-H "Authorization: Bearer merchant_private_key" \
-H "Content-Type: application/json" -d '{
"token" : "Your Product"
}'
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://business.cixsdpxj.info/api/v1/payments/decline",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "{ \"token\" : \"payment token\""\n}",
CURLOPT_HTTPHEADER => array(
"authorization: Bearer merchant_private_key",
"content-type: application/json"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
from django.http import HttpResponseRedirect, HttpResponse
import requests
import json
def pay(request) :
MERCHANT_PRIVATE_KEY = 'merchant_private_key'
LIVE_URL = 'https://business.cixsdpxj.info';
SANDBOX_URL = 'https://business.cixsdpxj.info'
payload = {
"token" : request.POST['token payment']
}
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer %s' % (MERCHANT_PRIVATE_KEY)
}
resp = requests.post('%s/api/v1/payments/decline' % (SANDBOX_URL), json=payload, headers=headers)
if resp.status_code == 200:
resp_payload = json.loads(resp.text)
return HttpResponseRedirect(resp_payload['processingUrl'])
else:
return HttpResponse('<html><body><span>Something gone wrong: %s</span></body></html>' % (resp.status_code))
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("token", "payment token");
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://business.cixsdpxj.info/api/v1/payments/decline")
.post(RequestBody.create(JSON, new Gson().toJson(params)))
.addHeader("content-type", "application/json")
.addHeader("authorization", "Bearer merchant_private_key")
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("response ", "onFailure(): " + e.getMessage() );
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String resp = response.body().string();
Log.e("response ", "onResponse(): " + resp );
}
});
Return status 200 and JSON: Copy
{
"success": true | false,
"result": 0,
"status": 200,
"payment": {
"amount": 100,
"gateway_amount": 100,
"currency": "USD",
"status": "approved|declined",
"two_stage_mode": true
}
}
Decline Two-Step payment by providing a payment token.
HTTP Request via SSL
POST '/api/v1/payments/decline'
Query Parameters
| Parameter | Mandatory | Description |
|---|---|---|
| token | yes | Payment token. |
Refunds
SPINPAY refunds processing REST API.
Create refund
Code: Copy
curl "https://business.cixsdpxj.info/api/v1/refunds" \
-X POST \
-H "Authorization: Bearer merchant_private_key" \
-H "Content-Type: application/json" -d '{
"token" : "Your Product",
"amount": 1000
}'
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://business.cixsdpxj.info/api/v1/payments",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "{ \"token\" : \"payment token\""\n}",
CURLOPT_HTTPHEADER => array(
"authorization: Bearer merchant_private_key",
"content-type: application/json"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
from django.http import HttpResponseRedirect, HttpResponse
import requests
import json
def pay(request) :
MERCHANT_PRIVATE_KEY = 'merchant_private_key'
LIVE_URL = 'https://business.cixsdpxj.info';
SANDBOX_URL = 'https://business.cixsdpxj.info'
payload = {
"token" : request.POST['token payment'],
"amount": 100
}
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer %s' % (MERCHANT_PRIVATE_KEY)
}
resp = requests.post('%s/api/v1/refunds' % (SANDBOX_URL), json=payload, headers=headers)
if resp.status_code == 200:
resp_payload = json.loads(resp.text)
return HttpResponseRedirect(resp_payload['processingUrl'])
else:
return HttpResponse('<html><body><span>Something gone wrong: %s</span></body></html>' % (resp.status_code))
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("token", "payment token");
params.put("amount", 100);
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://business.cixsdpxj.info/api/v1/refunds")
.post(RequestBody.create(JSON, new Gson().toJson(params)))
.addHeader("content-type", "application/json")
.addHeader("authorization", "Bearer merchant_private_key")
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("response ", "onFailure(): " + e.getMessage() );
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String resp = response.body().string();
Log.e("response ", "onResponse(): " + resp );
}
});
Return status 200 and JSON: Copy
{
"success": true | false,
"errors": [],
"token": "[payment token]",
"processingUrl": "https://business.cixsdpxj.info/p/[payment token]",
"refund": {
"token": "3a1a4fc8f975eb022a1c0ddb3abcded9",
"amount": "10020",
"currency": "USD",
"status": "approved|declined"
}
}
Create refunds by providing a payment token.
HTTP Request via SSL
POST '/api/v1/refunds'
Query Parameters
| Parameter | Mandatory | Description |
|---|---|---|
| token | yes | Payment token. |
| amount | no | Refund amount in cents. |
Payouts
Transferring money from a business account to a client account.
Make a payout
Code: Copy
curl "https://business.cixsdpxj.info/api/v1/payouts" \
-X POST \
-H "Authorization: Bearer merchant_private_key" \
-H "Content-Type: application/json" -d '{
"amount" : 1000,
"currency" : "CNY",
"orderNumber": "10001",
"extraReturnParam" : "test payout",
"card": {
"pan" : "4276111152393643",
"expires" : "08/2022"
},
"customer": {
"name" : "Mike",
"surname" : "Green",
"email" : "test@cixsdpxj.info",
"address" : "725 5th Ave, New York, NY 10022, United States",
"ip" : "1.1.1.1"
}
}'
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://business.cixsdpxj.info/api/v1/payouts",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "{ \"amount\" : 1000, \"currency\" : \"CNY\", \"orderNumber\" : \"10001\", \"extraReturnParam\" : \"test payout\", \"card\": { \"pan\" : \"4276111152393643\", \"expires\" : \"08/2022\" }, \"customer\": { \"email\" : \"test@cixsdpxj.info\", \"address\" : \"test test\", \"ip\" : \"1.1.1.1\"}"\n}",
CURLOPT_HTTPHEADER => array(
"authorization: Bearer edf526c5374796cdcec5dce731405cee",
"content-type: application/json"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
from django.shortcuts import render
from django.http import HttpResponseRedirect, HttpResponse, HttpResponseNotFound
from django.views.decorators.csrf import csrf_exempt
import requests
import json
def payout(request) :
MERCHANT_PRIVATE_KEY = 'your-merchant-private-key'
LIVE_URL = 'https://business.cixsdpxj.info';
SANDBOX_URL = 'https://business.cixsdpxj.info'
payload = {
"amount" : 10000,
"currency" : "EUR",
"orderNumber": "10001",
"card": {
"pan" : "4276111152393643",
"expires" : "08/2022"
},
"customer": {
"email" : "test@cixsdpxj.info",
"address" : "test test",
"ip" : "1.1.1.1"
}
}
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer %s' % (MERCHANT_PRIVATE_KEY)
}
resp = requests.post('%s/api/v1/payouts' % (SANDBOX_URL), json=payload, headers=headers)
if resp.status_code == 200:
resp_o = json.loads(resp.text)
return HttpResponseRedirect(resp_o['status'])
else:
return HttpResponse('<html><body><span>Something gone wrong: %s</span> : %s</body></html>' % (resp.status_code, resp.text))
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("amount", 1000);
params.put("currency", "EUR");
params.put("orderNumber", "[merchat system order number]");
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://business.cixsdpxj.info/api/v1/payouts")
.post(RequestBody.create(JSON, new Gson().toJson(params)))
.addHeader("content-type", "application/json")
.addHeader("authorization", "Bearer merchant_private_key")
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("response ", "onFailure(): " + e.getMessage() );
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String resp = response.body().string();
Log.e("response ", "onResponse(): " + resp );
}
});
Return status 200 and JSON: Copy
{
"success": true | false,
"errors": [],
"payout": {
"token": "[payment token]",
"status": "[payment status]",
"timestamp": "2016-06-09T03:46:45Z"
}
}
Create a payout operation.
HTTP Request via SSL
POST '/api/v1/payouts'
Query Parameters
| Parameter | Mandatory | Description |
|---|---|---|
| amount | yes | Payment amount in minimal values as of; USD and EUR / Cents, for JPY / Yen, for CNY / Fen. |
| currency | yes | Currency code (CNY, EUR, USD, JPY) |
| orderNumber | yes | SPINPAY's client inner order number |
| card | yes | Card object for Host2Host payouts. |
| customer | yes | Customer object for Host2Host payouts. |
Card Payout Object Parameters
| Parameter | Mandatory | Description |
|---|---|---|
| pan | yes | Customer’s card number (PAN). Any valid card number, may contain spaces |
| expires | yes | Customer’s card expiration date. Format: mm/yyyy |
Customer Object Parameters (optional)
| Parameter | Mandatory | Description |
|---|---|---|
| yes | Customer’s email, is mandatory if Customer object posted on a request | |
| address | no | Customer's billing address in the full format like "725 5th Ave, New York, NY 10022, United States" |
| ip | yes | Customer IP address |
| name | no | Customer name |
| surname | no | Customer surname |
Providers
Code: Copy
curl "https://business.cixsdpxj.info/api/v1/payouts" \
-X POST \
-H "Authorization: Bearer merchant_private_key" \
-H "Content-Type: application/json" -d '{
"amount" : 1000,
"currency" : "CNY",
"orderNumber": "10001",
"extraReturnParam" : "test payout",
"card": {
"pan" : "4276111152393643",
"expires" : "08/2022"
},
"customer": {
"email" : "test@cixsdpxj.info",
"address" : "test test",
"ip" : "1.1.1.1"
}
}'
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://business.cixsdpxj.info/api/v1/payouts",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "{ \"amount\" : 1000, \"currency\" : \"CNY\", \"orderNumber\" : \"10001\", \"extraReturnParam\" : \"test payout\", \"card\": { \"pan\" : \"4276111152393643\", \"expires\" : \"08/2022\" }, \"customer\": { \"email\" : \"test@cixsdpxj.info\", \"address\" : \"test test\", \"ip\" : \"1.1.1.1\"}"\n}",
CURLOPT_HTTPHEADER => array(
"authorization: Bearer edf526c5374796cdcec5dce731405cee",
"content-type: application/json"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
from django.shortcuts import render
from django.http import HttpResponseRedirect, HttpResponse, HttpResponseNotFound
from django.views.decorators.csrf import csrf_exempt
import requests
import json
def payout(request) :
MERCHANT_PRIVATE_KEY = 'your-merchant-private-key'
LIVE_URL = 'https://business.cixsdpxj.info';
SANDBOX_URL = 'https://business.cixsdpxj.info'
payload = {
"amount" : 10000,
"currency" : "EUR",
"orderNumber": "10001",
"card": {
"pan" : "4276111152393643",
"expires" : "08/2022"
},
"customer": {
"email" : "test@cixsdpxj.info",
"address" : "test test",
"ip" : "1.1.1.1"
}
}
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer %s' % (MERCHANT_PRIVATE_KEY)
}
resp = requests.post('%s/api/v1/payouts' % (SANDBOX_URL), json=payload, headers=headers)
if resp.status_code == 200:
resp_o = json.loads(resp.text)
return HttpResponseRedirect(resp_o['status'])
else:
return HttpResponse('<html><body><span>Something gone wrong: %s</span> : %s</body></html>' % (resp.status_code, resp.text))
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("amount", 1000);
params.put("currency", "EUR");
params.put("orderNumber", "[merchat system order number]");
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://business.cixsdpxj.info/api/v1/payouts")
.post(RequestBody.create(JSON, new Gson().toJson(params)))
.addHeader("content-type", "application/json")
.addHeader("authorization", "Bearer merchant_private_key")
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("response ", "onFailure(): " + e.getMessage() );
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String resp = response.body().string();
Log.e("response ", "onResponse(): " + resp );
}
});
Return status 200 and JSON: Copy
{
"success": true | false,
"errors": [],
"token": "[payment token]",
"processingUrl": [
{
"webmoney": "http://business.cixsdpxj.info/pout/165998589a413b56ae72fbfdc15b016b/webmoney?locale=en"
},
{
"bank_card": "http://business.cixsdpxj.info/pout/165998589a413b56ae72fbfdc15b016b/bank_card?locale=en"
},
{
"qiwi_wallet": "http://business.cixsdpxj.info/pout/165998589a413b56ae72fbfdc15b016b/qiwi_wallet?locale=en"
},
{
"skrill_wallet": "http://business.cixsdpxj.info/pout/165998589a413b56ae72fbfdc15b016b/skrill_wallet?locale=en"
}
],
"selectorURL": "https://business.cixsdpxj.info/select/pout/[payment token]/",
"payment": {
"amount": "10020",
"currency": "CNY",
"status": "init"
}
}
In case multiple payout providers enabled to a merchant account, Create payout reponse JSON will have processingUrl object represented as an array of available payout providers (please refer to JSON response). Use those URLs to redirect your customer to a payout provider (method).
List of payout providers
In case you want a customer to choose a payout provider (method) it might be convenient to use a specific page (widget) with payout provider list, which is availabe by "selectorURL" parameter in JSON response object
P2P
SPINPAY p2p payment processing REST API.
For payouts need use object processingUrl from response.
Object selectorUrl from response use only for payin.
Payment
Code: Copy
curl "https://business.cixsdpxj.info/api/v1/payments" \
-X POST \
-H "Authorization: Bearer merchant_private_key" \
-H "Content-Type: application/json" -d '{
"product": "Tests",
"amount": 100300,
"currency": "RUB",
"callbackUrl": "https://test.com",
"redirectSuccessUrl": "https://success.test.com/",
"redirectFailUrl": "https://declined.test.com/",
"bank_account": {
"bank_name": "sber",
"requisite_type": "sbp/card/account"
},
"customer": {
"email": "test@test.com",
"ip": "178.175.50.34"
}
}'
<?php
$curl = curl_init();
$data = [
"product" => "Tests",
"amount" => 100300,
"currency" => "RUB",
"callbackUrl" => "https://test.com",
"redirectSuccessUrl" => "https://success.test.com/",
"redirectFailUrl" => "https://declined.test.com/",
"bank_account" => [
"bank_name" => "sber",
"requisite_type" => "sbp/card/account"
],
"customer" => [
"email" => "test@test.com",
"ip" => "178.175.50.34"
]
];
curl_setopt_array($curl, array(
CURLOPT_URL => "https://business.cixsdpxj.info/api/v1/payments",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => array(
"authorization: Bearer merchant_private_key",
"content-type: application/json"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
?>
from django.http import HttpResponseRedirect, HttpResponse
import requests
import json
def pay(request) :
MERCHANT_PRIVATE_KEY = 'merchant_private_key'
LIVE_URL = 'https://business.cixsdpxj.info';
SANDBOX_URL = 'https://business.cixsdpxj.info'
payload = {
"product": "Tests",
"amount": 100300,
"currency": "RUB",
"callbackUrl": "https://test.com",
"redirectSuccessUrl": "https://success.test.com/",
"redirectFailUrl": "https://declined.test.com/",
"bank_account": {
"bank_name": "sber",
"requisite_type": "sbp/card/account"
},
"customer": {
"email": "test@test.com",
"ip": "178.175.50.34"
}
}
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer %s' % (MERCHANT_PRIVATE_KEY)
}
resp = requests.post('%s/api/v1/payments' % (SANDBOX_URL), json=payload, headers=headers)
if resp.status_code == 200:
resp_payload = json.loads(resp.text)
return HttpResponseRedirect(resp_payload['processingUrl'])
else:
return HttpResponse('<html><body><span>Something gone wrong: %s</span></body></html>' % (resp.status_code))
import com.google.gson.Gson;
import okhttp3.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
Map<String, Object> params = new HashMap<>();
params.put("product", "Tests");
params.put("amount", 100300);
params.put("currency", "RUB");
params.put("callbackUrl", "https://test.com");
params.put("redirectSuccessUrl", "https://success.test.com/");
params.put("redirectFailUrl", "https://declined.test.com/");
Map<String, String> bankAccount = new HashMap<>();
bankAccount.put("bank_name", "sber");
bankAccount.put("requisite_type", "sbp/card");
params.put("bank_account", bankAccount);
Map<String, String> customer = new HashMap<>();
customer.put("email", "test@test.com");
customer.put("ip", "178.175.50.34");
params.put("customer", customer);
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://business.cixsdpxj.info/api/v1/payments")
.post(RequestBody.create(JSON, new Gson().toJson(params)))
.addHeader("content-type", "application/json")
.addHeader("authorization", "Bearer merchant_private_key")
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e)
;
Return status 200 and JSON: Copy
{
"success": true,
"result": 0,
"status": 200,
"token": "K9doc43HAPnqSuGdvN5RMX42tE54331",
"processingUrl": [
{
"trader": "https://business.cixsdpxj.info/p/K9doc43HAPnqSuGdvN5RMX42tE54331?..."
}
],
"selectorUrl": "https://business.cixsdpxj.info/select/K9doc43HAPnqSuGdvN5RMX42tE54331?...",
"payment": {
"amount": 1000,
"gateway_amount": 1000,
"currency": "RUB",
"status": "init",
"two_stage_mode": false,
"commission": 0
}
}
Response from trader in processingUrl (card): Copy
{
"success": true,
"token": "EVgmP4KgXGbcUwQECfx5T2ZFbRq6EnWq",
"processingUrl": "https://business.cixsdpxj.info/checkout_results/K9doc43HAPnqSuGdvN5RMX42tE54331/processing",
"payment": {
"amount": 33300,
"currency": "RUB",
"gateway_amount": 370,
"gateway_currency": "USDT",
"status": "pending"
},
"card": {
"pan": "4111111111111111"
}
}
Response from trader in processingUrl (sbp): Copy
{
"success": true,
"token": "EVgmP4KgXGbcUwQECfx5T2ZFbRq6EnWq",
"processingUrl": "https://business.cixsdpxj.info/checkout_results/K9doc43HAPnqSuGdvN5RMX42tE54331/processing",
"payment": {
"amount": 33300,
"currency": "RUB",
"gateway_amount": 370,
"gateway_currency": "USDT",
"status": "pending"
},
"sbp": {
"phone": "79999999999",
"name": "Иванов И."
"bank" "Сбербанк"
}
}
Response from trader in processingUrl (account): Copy
{
"success": true,
"token": "EVgmP4KgXGbcUwQECfx5T2ZFbRq6EnWq",
"processingUrl": "https://business.cixsdpxj.info/checkout_results/K9doc43HAPnqSuGdvN5RMX42tE54331/processing",
"payment": {
"amount": 33300,
"currency": "RUB",
"gateway_amount": 370,
"gateway_currency": "USDT",
"status": "pending"
},
"account": {
"number": "44444444444444444444",
"name": "Anton B.",
"bank": "raiffesen"
}
}
Initialize payments - to begin receiving payments, you must first call using the following script. This will enable you to obtain a payment token, which will be required later to complete API integration. Use redirect/GET to processingUrl after request.
For H2H (host2host) integration use processingUrl from response.
For HPP (host payment page) integration use selectorUrl from response.
HTTP Request via SSL
POST '/api/v1/payments'
Query Parameters
| Parameter | Mandatory | Description | Validation |
|---|---|---|---|
| product | yes | Product name (Service description) (example: 'iPhone'). | minLength: 5, maxLength: 255 |
| amount | yes | Payment amount in cents (10020), except JPY | minLength: 1, maxLength: 32 |
| currency | yes | Currency code (CNY, EUR, USD, JPY). | minLength: 3, maxLength: 3 |
| callbackUrl | yes | The server URL a merchant will be notified about a payment finalisation | Valid URI format |
| redirectSuccessUrl | no | The URL a customer will be redirected to in the case of successfull payment | Valid URI format |
| redirectFailUrl | no | The URL a customer will be redirected to in the case of payment error or failure | Valid URI format |
| extraReturnParam | no | Bank/Payment method list, description, etc | minLength: 1, maxLength: 1024 |
| orderNumber | no | The current order number from a company system. | minLength: 3, maxLength: 255 (string) |
| locale | no | The locale is used on a payment page by default. Currently supported locales: en, zh and jp from ISO 639-1. | minLength: 2, maxLength: 5 (string) |
| bank_account | no | Bank details object for p2p payments. | |
| customer | yes | Customer object for payments. |
Bank account Payment Object Parameters
| Parameter | Mandatory | Description |
|---|---|---|
| bank_name | no | Customer bank name: sberbank/tbank/raiffeisen/uralsib/alfabank/sovcombank/humo/uzcard |
| requisite_type | no | Requisite type for payin: sbp/card/account |
Customer Object Parameters
| Parameter | Mandatory | Description | Validation |
|---|---|---|---|
| yes | Customer’s email | Valid email format | |
| ip | no | Customer IP address | Valid IP address format (XX.XX.XX.XX) |
Payout Card
Code: Copy
curl "https://business.cixsdpxj.info/api/v1/payouts" \
-X POST \
-H "Authorization: Bearer merchant_private_key" \
-H "Content-Type: application/json" -d '{
"amount" : 1000,
"currency" : "RUB",
"orderNumber": "10001",
"callbackUrl": "https://test.com",
"bank_account": {
"bank_name": "sber",
"requisite_type": "card"
},
"card": {
"pan": "4627342642639018"
},
"customer": {
"email": "test@test.com",
"ip": "178.175.20.33",
"first_name": "Иванов",
"last_name": "Иван",
"middle_name": "Иванович",
"phone": "79998889900"
}
}'
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://business.cixsdpxj.info/api/v1/payouts",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "{ \"amount\" : 1000, \"currency\" : \"RUB\", \"orderNumber\" : \"10001\", \"extraReturnParam\" : \"test payout\", \"bank_account\": { \"bank_name\" : \"sber\", \"requisite_type\" : \"card\" }, \"card\": { \"pan\" : \"4276111152393643\" }, \"customer\": { \"email\" : \"test@cixsdpxj.info\", \"address\" : \"test test\", \"ip\" : \"1.1.1.1\"}"\n}",
CURLOPT_HTTPHEADER => array(
"authorization: Bearer edf526c5374796cdcec5dce731405cee",
"content-type: application/json"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
from django.shortcuts import render
from django.http import HttpResponseRedirect, HttpResponse, HttpResponseNotFound
from django.views.decorators.csrf import csrf_exempt
import requests
import json
def payout(request) :
MERCHANT_PRIVATE_KEY = 'your-merchant-private-key'
LIVE_URL = 'https://business.cixsdpxj.info';
SANDBOX_URL = 'https://business.cixsdpxj.info'
payload = {
"amount" : 1000,
"currency" : "RUB",
"orderNumber": "10001",
"callbackUrl": "https://test.com",
"bank_account": {
"bank_name": "sber",
"requisite_type": "card"
},
"card": {
"pan": "4627342642639018"
},
"customer": {
"email": "test@test.com",
"ip": "178.175.20.33",
"first_name": "Иванов",
"last_name": "Иван",
"middle_name": "Иванович",
"phone": "79998889900"
}
}
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer %s' % (MERCHANT_PRIVATE_KEY)
}
resp = requests.post('%s/api/v1/payouts' % (SANDBOX_URL), json=payload, headers=headers)
if resp.status_code == 200:
resp_o = json.loads(resp.text)
return HttpResponseRedirect(resp_o['status'])
else:
return HttpResponse('<html><body><span>Something gone wrong: %s</span> : %s</body></html>' % (resp.status_code, resp.text))
import com.google.gson.Gson;
import okhttp3.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
Map<String, Object> params = new HashMap<>();
params.put("amount", 1000);
params.put("currency", "RUB");
params.put("orderNumber", "10001");
params.put("callbackUrl", "https://test.com");
Map<String, String> bankAccount = new HashMap<>();
bankAccount.put("bank_name", "sber");
bankAccount.put("requisite_type", "card");
params.put("bank_account", bankAccount);
Map<String, String> card = new HashMap<>();
card.put("pan", "4627342642639018");
params.put("card", card);
Map<String, String> customer = new HashMap<>();
customer.put("email", "test@test.com");
customer.put("ip", "178.175.20.33");
customer.put("first_name", "Иванов");
customer.put("last_name", "Иван");
customer.put("middle_name", "Иванович");
customer.put("phone", "79998889900");
params.put("customer", customer);
OkHttpClient client = new OkHttpClient();
// Создаем запрос
Request request = new Request.Builder()
.url("https://business.cixsdpxj.info/api/v1/payouts")
.post(RequestBody.create(JSON, new Gson().toJson(params)))
.addHeader("content-type", "application/json")
.addHeader("authorization", "Bearer merchant_private_key")
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
System.err.println("onFailure(): " + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String resp = response.body().string();
System.out.println("onResponse(): " + resp);
}
});
}
}
;
Return status 200 and JSON: Copy
{
"success": true,
"result": 0,
"status": 200,
"token": "c3452f792233e43aeaa819c68766043",
"processingUrl": [
{
"trader": "https://business.cixsdpxj.info/payout/..."
}
],
"selectorUrl": "https://business.cixsdpxj.info/select/payout/c3452f792233e43aeaa819c68766043",
"payment": {
"amount": 30000,
"currency": "RUB",
"gateway_amount": 30000,
"gateway_currency": "RUB",
"status": "init"
}
}
Create a payout operation. Use GET to processingUrl after request.
HTTP Request via SSL
POST '/api/v1/payouts'
Query Parameters
| Parameter | Mandatory | Description |
|---|---|---|
| amount | yes | Payment amount in minimal values. ex: 123 RUB = 1.23 RUB. |
| currency | yes | Currency code |
| orderNumber | yes | SPINPAY's client inner order number |
| callbackUrl | no | merchat notification url |
| bank_account | yes | Bank details object for p2p payouts. |
| card | yes | Card object for card p2p payouts. |
| customer | yes | Customer object for payouts. |
Card Payout Object Parameters
| Parameter | Mandatory | Description |
|---|---|---|
| pan | yes | Customer’s card number (PAN). Any valid card number |
Bank account Payout Object Parameters
| Parameter | Mandatory | Description |
|---|---|---|
| bank_name | no | Customer bank name: sberbank/tbank/raiffeisen/uralsib/alfabank/sovcombank/humo/uzcard |
| requisite_type | yes | card |
Customer Object Parameters
| Parameter | Mandatory | Description |
|---|---|---|
| yes | Valid email format | |
| ip | yes | Customer IP address |
| first_name | no | Customer name |
| last_name | no | Customer surname |
| middle_name | no | Customer middle name |
| phone | no | Customer phone for sbp |
Payout SBP
Code: Copy
curl "https://business.cixsdpxj.info/api/v1/payouts" \
-X POST \
-H "Authorization: Bearer merchant_private_key" \
-H "Content-Type: application/json" -d '{
"amount" : 1000,
"currency" : "RUB",
"orderNumber": "10001",
"callbackUrl": "https://test.com",
"bank_account": {
"bank_name": "sber",
"requisite_type": "sbp"
},
"customer": {
"email": "test@test.com",
"ip": "178.175.20.33",
"first_name": "Иванов",
"last_name": "Иван",
"middle_name": "Иванович",
"phone": "79998889900"
}
}'
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://business.cixsdpxj.info/api/v1/payouts",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "{ \"amount\" : 1000, \"currency\" : \"RUB\", \"orderNumber\" : \"10001\", \"extraReturnParam\" : \"test payout\", \"bank_account\": { \"bank_name\" : \"sber\", \"requisite_type\" : \"sbp\" }, \"customer\": { \"email\" : \"test@cixsdpxj.info\",\"phone\" : \"79998889900\", \"address\" : \"test test\", \"ip\" : \"1.1.1.1\"}"\n}",
CURLOPT_HTTPHEADER => array(
"authorization: Bearer edf526c5374796cdcec5dce731405cee",
"content-type: application/json"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
from django.shortcuts import render
from django.http import HttpResponseRedirect, HttpResponse, HttpResponseNotFound
from django.views.decorators.csrf import csrf_exempt
import requests
import json
def payout(request) :
MERCHANT_PRIVATE_KEY = 'your-merchant-private-key'
LIVE_URL = 'https://business.cixsdpxj.info';
SANDBOX_URL = 'https://business.cixsdpxj.info'
payload = {
"amount" : 1000,
"currency" : "RUB",
"orderNumber": "10001",
"callbackUrl": "https://test.com",
"bank_account": {
"bank_name": "sber",
"requisite_type": "sbp"
},
"customer": {
"email": "test@test.com",
"ip": "178.175.20.33",
"first_name": "Иванов",
"last_name": "Иван",
"middle_name": "Иванович",
"phone": "79998889900"
}
}
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer %s' % (MERCHANT_PRIVATE_KEY)
}
resp = requests.post('%s/api/v1/payouts' % (SANDBOX_URL), json=payload, headers=headers)
if resp.status_code == 200:
resp_o = json.loads(resp.text)
return HttpResponseRedirect(resp_o['status'])
else:
return HttpResponse('<html><body><span>Something gone wrong: %s</span> : %s</body></html>' % (resp.status_code, resp.text))
import com.google.gson.Gson;
import okhttp3.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
Map<String, Object> params = new HashMap<>();
params.put("amount", 1000);
params.put("currency", "RUB");
params.put("orderNumber", "10001");
params.put("callbackUrl", "https://test.com");
Map<String, String> bankAccount = new HashMap<>();
bankAccount.put("bank_name", "sber");
bankAccount.put("requisite_type", "sbp");
params.put("bank_account", bankAccount);
Map<String, String> customer = new HashMap<>();
customer.put("email", "test@test.com");
customer.put("ip", "178.175.20.33");
customer.put("first_name", "Иванов");
customer.put("last_name", "Иван");
customer.put("middle_name", "Иванович");
customer.put("phone", "79998889900");
params.put("customer", customer);
OkHttpClient client = new OkHttpClient();
// Создаем запрос
Request request = new Request.Builder()
.url("https://business.cixsdpxj.info/api/v1/payouts")
.post(RequestBody.create(JSON, new Gson().toJson(params)))
.addHeader("content-type", "application/json")
.addHeader("authorization", "Bearer merchant_private_key")
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
System.err.println("onFailure(): " + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String resp = response.body().string();
System.out.println("onResponse(): " + resp);
}
});
}
}
;
Return status 200 and JSON: Copy
{
"success": true,
"result": 0,
"status": 200,
"token": "c3452f792233e43aeaa819c68766043",
"processingUrl": [
{
"trader": "https://business.cixsdpxj.info/payout/..."
}
],
"selectorUrl": "https://business.cixsdpxj.info/select/payout/c3452f792233e43aeaa819c68766043",
"payment": {
"amount": 30000,
"currency": "RUB",
"gateway_amount": 30000,
"gateway_currency": "RUB",
"status": "init"
}
}
Create a payout operation. Use GET to processingUrl after request.
HTTP Request via SSL
POST '/api/v1/payouts'
Query Parameters
| Parameter | Mandatory | Description |
|---|---|---|
| amount | yes | Payment amount in minimal values. ex: 123 RUB = 1.23 RUB. |
| currency | yes | Currency code |
| orderNumber | yes | SPINPAY's client inner order number |
| callbackUrl | no | merchat notification url |
| bank_account | yes | Bank details object for p2p payouts. |
| customer | yes | Customer object for payouts. |
Bank account Payout Object Parameters
| Parameter | Mandatory | Description |
|---|---|---|
| bank_name | yes | Customer bank name: sberbank/tbank/raiffeisen/uralsib/alfabank/sovcombank/humo/uzcard |
| requisite_type | yes | sbp |
Customer Object Parameters
| Parameter | Mandatory | Description |
|---|---|---|
| yes | Valid email format | |
| ip | yes | Customer IP address |
| first_name | no | Customer name |
| last_name | no | Customer surname |
| middle_name | no | Customer middle name |
| phone | yes | Customer phone for sbp |
Payout Account
Code: Copy
curl "https://business.cixsdpxj.info/api/v1/payouts" \
-X POST \
-H "Authorization: Bearer merchant_private_key" \
-H "Content-Type: application/json" -d '{
"amount" : 1000,
"currency" : "RUB",
"orderNumber": "10001",
"callbackUrl": "https://test.com",
"bank_account": {
"bank_name": "sber",
"requisite_type": "account",
"account_number": "1234567891123456"
},
"customer": {
"email": "test@test.com",
"ip": "178.175.20.33",
"first_name": "Иванов",
"last_name": "Иван",
"middle_name": "Иванович",
"phone": "79998889900"
}
}'
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://business.cixsdpxj.info/api/v1/payouts",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "{ \"amount\" : 1000, \"currency\" : \"RUB\", \"orderNumber\" : \"10001\", \"extraReturnParam\" : \"test payout\", \"bank_account\": { \"bank_name\" : \"sber\", \"requisite_type\" : \"account\", \"account_number\" : \"1234567891123456\" }, \"customer\": { \"email\" : \"test@cixsdpxj.info\", \"address\" : \"test test\", \"ip\" : \"1.1.1.1\"}"\n}",
CURLOPT_HTTPHEADER => array(
"authorization: Bearer edf526c5374796cdcec5dce731405cee",
"content-type: application/json"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
from django.shortcuts import render
from django.http import HttpResponseRedirect, HttpResponse, HttpResponseNotFound
from django.views.decorators.csrf import csrf_exempt
import requests
import json
def payout(request) :
MERCHANT_PRIVATE_KEY = 'your-merchant-private-key'
LIVE_URL = 'https://business.cixsdpxj.info';
SANDBOX_URL = 'https://business.cixsdpxj.info'
payload = {
"amount" : 1000,
"currency" : "RUB",
"orderNumber": "10001",
"callbackUrl": "https://test.com",
"bank_account": {
"bank_name": "sber",
"requisite_type": "account",
"account_number": "1234567891123456"
},
"customer": {
"email": "test@test.com",
"ip": "178.175.20.33",
"first_name": "Иванов",
"last_name": "Иван",
"middle_name": "Иванович",
"phone": "79998889900"
}
}
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer %s' % (MERCHANT_PRIVATE_KEY)
}
resp = requests.post('%s/api/v1/payouts' % (SANDBOX_URL), json=payload, headers=headers)
if resp.status_code == 200:
resp_o = json.loads(resp.text)
return HttpResponseRedirect(resp_o['status'])
else:
return HttpResponse('<html><body><span>Something gone wrong: %s</span> : %s</body></html>' % (resp.status_code, resp.text))
import com.google.gson.Gson;
import okhttp3.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
Map<String, Object> params = new HashMap<>();
params.put("amount", 1000);
params.put("currency", "RUB");
params.put("orderNumber", "10001");
params.put("callbackUrl", "https://test.com");
Map<String, String> bankAccount = new HashMap<>();
bankAccount.put("bank_name", "sber");
bankAccount.put("requisite_type", "account");
bankAccount.put("account_number","1234567891123456")
params.put("bank_account", bankAccount);
Map<String, String> customer = new HashMap<>();
customer.put("email", "test@test.com");
customer.put("ip", "178.175.20.33");
customer.put("first_name", "Иванов");
customer.put("last_name", "Иван");
customer.put("middle_name", "Иванович");
customer.put("phone", "79998889900");
params.put("customer", customer);
OkHttpClient client = new OkHttpClient();
// Создаем запрос
Request request = new Request.Builder()
.url("https://business.cixsdpxj.info/api/v1/payouts")
.post(RequestBody.create(JSON, new Gson().toJson(params)))
.addHeader("content-type", "application/json")
.addHeader("authorization", "Bearer merchant_private_key")
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
System.err.println("onFailure(): " + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String resp = response.body().string();
System.out.println("onResponse(): " + resp);
}
});
}
}
;
Return status 200 and JSON: Copy
{
"success": true,
"result": 0,
"status": 200,
"token": "c3452f792233e43aeaa819c68766043",
"processingUrl": [
{
"trader": "https://business.cixsdpxj.info/payout/..."
}
],
"selectorUrl": "https://business.cixsdpxj.info/select/payout/c3452f792233e43aeaa819c68766043",
"payment": {
"amount": 30000,
"currency": "RUB",
"gateway_amount": 30000,
"gateway_currency": "RUB",
"status": "init"
}
}
Create a payout operation. Use GET to processingUrl after request.
HTTP Request via SSL
POST '/api/v1/payouts'
Query Parameters
| Parameter | Mandatory | Description |
|---|---|---|
| amount | yes | Payment amount in minimal values. ex: 123 RUB = 1.23 RUB. |
| currency | yes | Currency code |
| orderNumber | yes | SPINPAY's client inner order number |
| callbackUrl | no | merchat notification url |
| bank_account | yes | Bank details object for p2p payouts. |
| customer | yes | Customer object for payouts. |
Bank account Payout Object Parameters
| Parameter | Mandatory | Description |
|---|---|---|
| bank_name | yes | Customer bank name: sberbank/tbank/raiffeisen/uralsib/alfabank/sovcombank/humo/uzcard |
| account_number | yes | Customer’s account number. Any valid account number |
| requisite_type | yes | account |
Customer Object Parameters
| Parameter | Mandatory | Description |
|---|---|---|
| yes | Valid email format | |
| ip | yes | Customer IP address |
| first_name | no | Customer name |
| last_name | no | Customer surname |
| middle_name | no | Customer middle name |
| phone | no | Customer phone for sbp |
Disputes
Request current SPINPAY dispute list.
Dispute list
Code: Copy
curl "https://business.cixsdpxj.info/api/v1/disputes" \
-X GET \
-H "Authorization: Bearer merchant_private_key" \
-H "Content-Type: application/json"
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://business.cixsdpxj.info/api/v1/disputes",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => array(
"authorization: Bearer merchant_private_key",
"content-type: application/json"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
from django.shortcuts import render
from django.http import HttpResponseRedirect, HttpResponse, HttpResponseNotFound
from django.views.decorators.csrf import csrf_exempt
import requests
import json
def disputes(request) :
MERCHANT_PRIVATE_KEY = 'merchant_private_key'
LIVE_URL = 'https://business.cixsdpxj.info';
SANDBOX_URL = 'https://business.cixsdpxj.info'
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer %s' % (MERCHANT_PRIVATE_KEY)
}
resp = requests.get('%s/api/v1/disputes' % (SANDBOX_URL), headers=headers)
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://business.cixsdpxj.info/api/v1/disputes")
.get()
.addHeader("content-type", "application/json")
.addHeader("authorization", "Bearer merchant_private_key")
.build();
Response response = client.newCall(request).execute();
Return status 200 and JSON: Copy
{
"success": true,
"status": 200,
"disputes": [
{
"id": 27,
"amount": 2,
"currency": "USD",
"investigation_report": null,
"status": "processing",
"merchant_profile_id": 3,
"user_profile_id": 3,
"feed_id": 330,
"created_at": "2019-09-13T08:46:21.302Z",
"updated_at": "2019-09-13T08:46:21.343Z",
"dispute_type": 2,
"reason_code": "123",
"comment": "some comment"
}
]
}
Getting a list of last disputes for a business account.
HTTP Request via SSL
GET '/api/v1/disputes'
Query Parameters
Returns 100 latest records
| Parameter | Mandatory | Description |
|---|---|---|
| status | no | Dispute status for filter [approved/pending/declined] |
| date | no | Date for filter |
| requisite | no | Requisite for filter |
| device | no | Device for filter |
Create a dispute
Code: Copy
curl "https://business.cixsdpxj.info/api/v1/disputes" \
-X POST \
-header "Authorization: Bearer merchant_private_key" \
--header 'Content-Type: application/json' \
--form 'token="XsKkj4kmSjNATwoJdoiwwCmEKbT5efZX"' \
--form 'description="test description"' \
--form 'document=@"/home/test/Pictures/Screenshot from 2024-04-12 17-38-19.png"'
}'
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://business.cixsdpxj.info/api/v1/disputes",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "{ \"token\" : XsKkj4kmSjNATwoJdoiwwCmEKbT5efZX, \"description\" : \"test description\", \"document\" : \"@"/home/test/Pictures/Screenshot from 2024-04-12 17-38-19.png"\"}",
CURLOPT_HTTPHEADER => array(
"authorization: Bearer edf526c5374796cdcec5dce731405cee",
"content-type: application/json"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
from django.shortcuts import render
from django.http import HttpResponseRedirect, HttpResponse, HttpResponseNotFound
from django.views.decorators.csrf import csrf_exempt
import requests
import json
def payout(request) :
MERCHANT_PRIVATE_KEY = 'your-merchant-private-key'
LIVE_URL = 'https://business.cixsdpxj.info';
SANDBOX_URL = 'https://business.cixsdpxj.info'
payload = {
"token" : "XsKkj4kmSjNATwoJdoiwwCmEKbT5efZX",
"description" : "test description",
"document": ""/home/test/Pictures/Screenshot from 2024-04-12 17-38-19.png""
}
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer %s' % (MERCHANT_PRIVATE_KEY)
}
resp = requests.post('%s/api/v1/disputes' % (SANDBOX_URL), json=payload, headers=headers)
if resp.status_code == 200:
resp_o = json.loads(resp.text)
return HttpResponseRedirect(resp_o['status'])
else:
return HttpResponse('<html><body><span>Something gone wrong: %s</span> : %s</body></html>' % (resp.status_code, resp.text))
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("token", "XsKkj4kmSjNATwoJdoiwwCmEKbT5efZX");
params.put("description", "test description");
params.put("document", "/home/test/Pictures/Screenshot from 2024-04-12 17-38-19.png");
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://business.cixsdpxj.info/api/v1/disputes")
.post(RequestBody.create(JSON, new Gson().toJson(params)))
.addHeader("content-type", "application/json")
.addHeader("authorization", "Bearer merchant_private_key")
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("response ", "onFailure(): " + e.getMessage() );
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String resp = response.body().string();
Log.e("response ", "onResponse(): " + resp );
}
});
Return status 200 and JSON: Copy
{
"success": true | false,
"result": 0,
"status": 200
}
Create a dispute.
HTTP Request via SSL
POST '/api/v1/disputes'
Query Parameters
| Parameter | Mandatory | Description |
|---|---|---|
| token | yes | Payment token |
| description | yes | Description for dispute |
| document | yes | Payment receipt for dispute |
| amount | no | Amount for dispute |
Balance
Request current SPINPAY balance.
Receive Balance
Code: Copy
curl "https://business.cixsdpxj.info/api/v1/balance?currency=CNY" \
-X GET \
-H "Authorization: Bearer merchant_private_key" \
-H "Content-Type: application/json"
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://business.cixsdpxj.info/api/v1/balance?currency=CNY",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => array(
"authorization: Bearer merchant_private_key",
"content-type: application/json"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
from django.shortcuts import render
from django.http import HttpResponseRedirect, HttpResponse, HttpResponseNotFound
from django.views.decorators.csrf import csrf_exempt
import requests
import json
def balance(request) :
MERCHANT_PRIVATE_KEY = 'merchant_private_key'
LIVE_URL = 'https://business.cixsdpxj.info';
SANDBOX_URL = 'https://business.cixsdpxj.info'
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer %s' % (MERCHANT_PRIVATE_KEY)
}
resp = requests.get('%s/api/v1/balance' % (SANDBOX_URL), params = {'currency':'CNY'}, headers=headers)
if resp.success:
resp_o = json.loads(resp.text)
return HttpResponse('<html><body><span>Your balance %s</body></html>' % (resp_o['wallet']['available']))
else:
return HttpResponse('<html><body><span>Something gone wrong: %s</span> : %s</body></html>' % (resp.status_code, resp.text))
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://business.cixsdpxj.info/api/v1/balance?currency=CNY")
.get()
.addHeader("content-type", "application/json")
.addHeader("authorization", "Bearer merchant_private_key")
.build();
Response response = client.newCall(request).execute();
Return status 200 and JSON: Copy
{
"success": true | false,
"errors": [],
"wallet": {
"available": 0,
"hold": 0,
"currency": "CNY"
}
}
Receiving the balance for a business account. Balance is returned as an object displaying available and pending amounts. Balances shown may be not be released and/or processed.
HTTP Request via SSL
GET '/api/v1/balance'
Query Parameters
| Parameter | Description |
|---|---|
| currency | Currency code (CNY) |
Status
SPINPAY payment/payout/refund status information.
Get
Code: Copy
curl "https://business.cixsdpxj.info/api/v1/payments/[payment_token]" \
-H "Authorization: Bearer merchant_private_key"
Return status 200 and JSON: Copy
{
"success": true | false,
"errors": [],
"status": 200,
"payment": {
"id": 2599,
"status": "pending | approved | declined",
"token": "[payment token]",
"currency": "[payment currency]",
"product": "[product description]",
"callback_url": "[callback/notification url]",
"redirect_success_url": "success redirection url",
"redirect_fail_url": "fail redirection url",
"amount": 0,
"created_at": "[creation date]",
"updated_at": "[last status update date]",
"extra_return_param": "[extra params, can be use to payment identification in merchat system]",
"operation_type": "pay | payout",
"order_number": "[merchant's order number]"
}
}
Payment Get - this is the method used to retrieve information about single payment.
HTTP Request via SSL
GET '/api/v1/payments/[payment_token]'
Get/Order
Code: Copy
curl "https://business.cixsdpxj.info/api/v1/payments/order/[order_number]" \
-H "Authorization: Bearer merchant_private_key"
Return status 200 and JSON: Copy
{
"success": true | false,
"result": 0,
"status": 200,
"totalCount": 1,
"curentPage": 1,
"perPage": 100,
"totalPage": 1,
"payments": [
{
"id": 123,
"status": "pending | approved | declined | expired",
"token": "[payment token]",
"currency": "[payment currency]",
"product": "[payment currency]",
"callback_url": "[callback/notification url]",
"redirect_success_url": "success redirection url",
"redirect_fail_url": "fail redirection url",
"amount": 100,
"created_at": "[creation date]",
"updated_at": "[last status update date]",
"extra_return_param": "[extra params, can be use to payment identification in merchant system]",
"operation_type": "pay | payout",
"order_number": "[merchant's order number]"
}
]
}
Payment Get/Order - this is the method used to retrieve information about payments by order_number.
HTTP Request via SSL
GET '/api/v1/payments/order/[order_number]'
Notifications
Notifications with the payment or payout status are sent to your callback URL using POST methods. In case payment or payout status changed (pending/approved/declined) -- notification type is sent accordingly.
Code: Copy
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponseRedirect, HttpResponse, HttpResponseNotFound
@csrf_exempt
def notifyme(request) :
req_o = json.loads(request.read());
return HttpResponse('Status is:%s' % (req_o['status']))
Params: Copy
{
"token": "payment token",
"type": "payment type: payment | payout",
"status": "payment status: pending | approved | declined | expired,
"extraReturnParam": "extra params",
"orderNumber": "merchant order number",
"walletToken": "payer's SPINPAY wallet unique identifier, only for IPS payments",
"recurringToken": "payer's previously initialized recurring token, for making recurrent payment repeatedly",
"sanitizedMask": "payer's sanitized card, if it was provided",
"cardHolder": "card holder for cards payments"
"amount": "payment amount in cents",
"currency": "payment currency",
"gatewayAmount": "exchanged amount in cents",
"gatewayCurrency": "exchanged currency",
"gatewayDetails": "additional payment details",
"signature": "callback signature"
}
Signature from Notification
The signature from the callback is checked as follows:
The sum of all parameters, which consists of the length of the string + the string itself. We encode the final string + merchant token in hashlib.md5
Code: Copy
['7ca152d572b003e0ab53e6b0637b4324', 'pay', 'approved', 'AutoTest_3781', 'AutoTest_9153', 1000, 'USD', '1000', 'USD']
32 + 7ca152d572b003e0ab53e6b0637b4324
3 + pay
8 + approved
13 + AutoTest_3781
13 + AutoTest_9153
4 + 1000
3 + USD
4 + 1000
3 + USD
signature_string = 327ca152d572b003e0ab53e6b0637b43243pay8approved13AutoTest_378113AutoTest_9153410003USD410003USD
signature_string = signature_string + env['BEARER']
signature = hashlib.md5(signature_string.encode('utf-8')).hexdigest()
return signature
Dictionaries
Errors
If any method failed, the JSON response with status code 403 returned that specified the problem.
Return status 403 and JSON: Copy
{'success': false, 'result': 1, 'status': 403, 'errors': {'list': [{'code': 'merchant_not_found', 'kind': 'api_error'}]}}
{'success': false, 'result': 1, 'status': 403, 'errors': [{'code': 'amount_less_than_minimum', 'kind': 'invalid_request_error'}]}
{'success': false, 'result': 1, 'status': 403, 'errors': [{'code': 'amount_less_than_balance', 'kind': 'processing_error'}]}
Payment states
| State | Final | Description |
|---|---|---|
| init | no | Request to API will initiate payments. |
| pending | no | User redirected to the SPINPAY Checkout facility during payment processing period. |
| approved | yes | Successfully completed payment. |
| declined | yes | Unsuccessful payment. |
| expired | yes | Expired or abandoned payment. |
Kinds of errors
| Kind | Description |
|---|---|
| api_error | Indicate rare occasions such as an SPINPAY API server technicality. |
| authentication_error | Authentication request failure. |
| invalid_request_error | Invalid parameters which produce invalid requests. |
| processing_error | Processing the payment generated an error. |
Codes of errors
| Code | Description |
|---|---|
| incorrect_private_key | The current private key cannot identify the user. |
| incorrect_address_info | Absent or incorrect address information. |
| incorrect_bank_card_info | Absent or incorrect bank card information. |
| order_number_already_exists | Repeating an order of already identified order number. |
| amount_less_than_balance | Payout cannot be completed due to insufficient funds. |
| incorrect_amount | Absent or incorrect amount value. |
| incorrect_currency | Absent or incorrect currency value. |
| incorrect_order_number | Absent or incorrect order value. |
| amount_less_than_minimum | Minimum payout amount has not been requested. |
Gateway.Connect
This document outlines the structure and purpose of gateway integration settings used for H2H (Host-to-Host) payments. These settings control how request parameters are wrapped, which fields are allowed, and define behavior per payment method.
General Structure for H2H
For H2H payments, parameters are wrapped using:
- Incoming request parameters
- Static settings defined per gateway
- Fields from the payments table in the database
Each payment method (pay, payout, refund, etc.) can have its own selector configuration.
Example Method Configuration
| Field | Type | Description |
|---|---|---|
final_waiting_seconds |
Integer | Seconds before initiating status check (default: 15) |
enable_status_checker |
Boolean | Enable payment status polling (default: true) |
params_fields |
Object | Configuration for request parameters |
params_fields.callback_3ds_url |
Boolean | 3DS callback URL (default: true) |
params_fields.callback_url |
Boolean | Generic callback URL (default: true) |
params_fields.processing_url |
Boolean | Final redirect URL (default: true) |
params_fields.params |
Array | Request parameters (see below) |
params_fields.payment |
Array | Allowed fields from the payments record (see below) |
params_fields.settings |
Array | Static config flags (see below) |
params_fields.params.pan |
String | Card PAN (default: true) |
params_fields.params.expires |
String | Card expiration (default: true) |
params_fields.params.holder |
String | Card holder name (default: true) |
params_fields.params.cvv |
String | CVV code (default: true) |
params_fields.params.email |
String | Customer email (default: true) |
params_fields.params.country |
String | Customer country (default: true) |
params_fields.params.city |
String | Customer city (default: true) |
params_fields.params.state |
String | Customer state (default: true) |
params_fields.params.phone |
String | Customer phone (default: true) |
params_fields.params.browser |
String | Browser data (JSON object) (default: true) |
params_fields.params.first_name |
String | Customer first name (default: true) |
params_fields.params.last_name |
String | Customer last name (default: true) |
params_fields.params.extra_return_param |
String | Extra return parameter (default: true) |
params_fields.payment.token |
String | Internal payment token (default: true) |
params_fields.payment.gateway_token |
String | External gateway token (default: true) |
params_fields.payment.gateway_amount |
String | Converted amount (default: true) |
params_fields.payment.gateway_currency |
String | Converted currency (default: true) |
params_fields.settings.sandbox |
Boolean | Enable sandbox (test) mode (default: true) |
"pay": {
"final_waiting_seconds": 15,
"enable_status_checker": true,
"params_fields": {
"callback_3ds_url": true,
"callback_url": true,
"processing_url": true,
"params": [
"pan",
"expires",
"holder",
"cvv",
"email",
"country",
"city",
"state",
"phone",
"browser",
"first_name",
"last_name",
"extra_return_param"
],
"payment": [
"token",
"gateway_token",
"gateway_amount",
"gateway_currency"
],
"settings": [
"sandbox"
]
}
}
Available parameter fields
| Field | Type | Description |
|---|---|---|
pan |
String | Card PAN |
expires |
String | Card expiration date |
holder |
String | Card holder name |
cvv |
String | Card CVV code |
email |
String | Customer email |
country |
String | Customer country |
city |
String | Customer city |
state |
String | Customer state |
postcode |
String | Customer postal code |
street |
String | Customer street address |
address |
String | Customer address |
region |
String | Customer region |
phone |
String | Customer phone number |
otp |
String | One-time password |
pin |
String | Personal identification number |
bank_code |
String | Bank code |
bank_account_number |
String | Bank account number |
identify_type |
String | Identification type |
identify_number |
String | Identification number |
recurring |
Boolean | Recurring payment flag |
need_confirmation |
Boolean | Confirmation requirement |
browser |
Object | Browser data (JSON object) |
country_code |
String | Customer country code |
first_name |
String | Customer first name |
father_name |
String | Customer father's name |
last_name |
String | Customer last name |
website |
String | Customer website |
birthday |
String | Customer birthday |
state |
String | Customer state |
recurring_data |
Object | Recurring payment data |
pending_url |
String | URL for pending payments |
extra_return_param |
String | Extra return parameter |
Available payment fields
| Field | Type | Description |
|---|---|---|
id |
Integer | Payment ID (we don't use it field) |
status |
String | Payment status |
token |
String | Internal payment token |
currency |
String | Origin payment currency |
product |
String | Product name |
callback_url |
String | Merchant callback URL |
redirect_success_url |
String | URL for successful payment redirect |
redirect_fail_url |
String | URL for failed payment redirect |
redirect_request |
String | URL for redirect request (non used) |
merchant_private_key |
String | Private key for Merchant record |
amount |
Integer | Payment amount |
gatewayable_type |
String | Name of gateway (e.g., `Gateway::TestpayPayment) |
gatewayable_id |
Integer | ID of gateway record |
created_at |
Datetime | Payment creation date |
updated_at |
Datetime | Payment update date |
extra_return_param |
String | Extra return parameter |
operation_type |
String | Payment operation type (e.g., pay, refund, payout) |
order_number |
String | Merchant order number |
declination_reason |
String | Reason for payment declination |
lead_id |
Integer | ID of the lead record |
ip |
String | Customer IP address |
browser_info |
String | Browser information |
bank_card_id |
Integer | ID of the bank card record |
kind |
String | Payment kind (e.g., wallet, direct) |
refund_id |
Integer | ID of the refund payment record |
scoring_remark |
String | Scoring remark |
country_code_by_BIN |
String | Country code by BIN (e.g., US) |
country_code_by_phone |
String | Country code by phone (e.g., US) |
country_code_by_IP |
String | Country code by IP (e.g., US) |
business_account_legal_name |
String | Business account legal name from merchant core settings |
business_account_profileID |
String | Business account profile ID from merchant core settings |
card_masked_number |
String | Masked card number (e.g., 1234****234) |
gateway_details |
JSON | Gateway-specific details (containts gateway_ids, redirect_request and other data`) |
gateway_currency |
String | Gateway currency after conversion |
gateway_amount |
Integer | Gateway amount after conversion |
details |
JSON | Additional details (e.g., request data, error data etc.) |
scoring_action |
String | Scoring action (e.g., success) |
scoring_action_log |
JSON | Scoring action log (e.g., {riskscore: {...}}) |
gateway_alias |
String | Gateway alias (e.g., testpay) |
card_brand_name |
String | Card brand name (e.g., Visa, MasterCard) |
routing_logs |
JSON | Routing logs (e.g., {prev_route: null {next_route...}}) |
gateway_token |
String | External gateway ID (e.g., 123456789) |
two_stage_mode |
Boolean | Indicates if the payment is in two-stage mode |
settings |
JSON | Payment settings (e.g., {status_checker_time_rates: {...}}) |
rrn |
String | RRN (Retrieval Reference Number) |
commission_data |
JSON | Commission data (e.g., {commission_fee: 10, commission_amount: 10}) |
notification_settings |
JSON | Notification settings (e.g., {"recipient"=>"test@test.tt", "allow_notification"=>true}) |
customer_country |
String | Customer country (e.g., US) |
trader_id |
Integer | Trader ID (e.g., 123456) |
merchant_url |
String | Merchant URL (e.g., http://example.com) |
reference |
String | Reference (e.g., 123456789) |
locale |
String | Locale (e.g., en-US) |
Allowed Methods (field "method")
| Field | Type | Description |
|---|---|---|
pay |
String | Payment initiatiate |
status |
String | Payment status check (if enable_status_checker is true) |
confirm_secure_code |
String | 3DS confirmation |
payout |
String | Outgoing payout |
refund |
String | Refund payment |
resend_otp |
String | Resend OTP code |
next_payment_step |
String | Next payment step for 3DS |
decline |
String | Decline payment |
For enabled methods need to be set in the methods array
"methods": [
"pay",
"status",
"confirm_secure_code",
"payout",
"refund"
]
Additional Top-Level Settings
| Field | Type | Description |
|---|---|---|
class |
String | Integration class name, matches container or routing name (e.g., testpay) |
processing_method |
String | Communication method: http_requests or rabbit (currently only HTTP) |
sandbox |
Boolean | Indicates whether gateway is in test/sandbox mode |
sign_key |
String | Secret used to sign callback JWTs |
status_checker_time_rates |
Object | Polling intervals per retry group |
Example Status Checker Time Rates
| status_checker_time_rate's count ranges | Interval (seconds) | Description |
|---|---|---|
1-3 |
30 | Every 30s for attempts 1 to 3 |
4-6 |
60 | Every 60s for attempts 4 to 6 |
7-9 |
120 | Every 120s for attempts 7 to 9 |
10- |
300 | Every 300s (5 min) for 10th attempt and later |
"status_checker_time_rates": {
"1-3": 30,
"4-6": 60,
"7-9": 120,
"10-": 300
}
This configuration controls how often the status of a payment is re-checked after creation.
Each setting allows you to tailor integration behavior for different gateway providers and payment types. These configurations ensure correct data handling, structured requests, and environment-specific behavior.
Gateway Routing Configuration
There describes the structure and purpose of the gateways_routing.yml configuration file. This file contains network-related settings for available integrations and their enabled status.
Configuration Location
/business/config/gateways_routing.yml
Example Structure
default:
gateway_routes:
testpay:
full_link: "http://test:9090"
host: test
port: 9090
enable: true
fastpaytest:
full_link: "https://gateway.cixsdpxj.info/p2p/fastpaytest"
host: gateway.cixsdpxj.info
port: 443
enable: true
gateway_key: fastpaytest
processing_method: http_requests
methods:
pay:
enable_status_checker: true
final_waiting_seconds: 15
params_fields:
callback_url: true
processing_url: true
params:
payment:
- gateway_token
- gateway_amount
- gateway_currency
settings:
- sandbox
- custom_host
- type_pay
Field Descriptions
| Field | Type | Description |
|---|---|---|
full_link |
String | Full URL of the gateway including protocol, host, and port |
host |
String | Hostname of the service (e.g., Docker container or domain name) |
port |
Integer | Port on which the service is listening |
enable |
Boolean | Indicates if the integration is enabled on the platform |
These settings are environment-agnostic and loaded for all environments by default unless overridden.
Settings example
{
"EUR": {
"gateways": {
"pay": {
"default": "testpay"
}
}
},
"gateways": {
"allow_host2host": true,
"testpay": {
"class": "testpay",
"sandbox": true,
"sign_key": "efc63a792a8375213898a166e50bc64d"
}
}
}
Flow of H2H Payments

Diagram of H2H Payment Flow

Required Endpoints
The external service must respond to the following endpoints:
/pay/payout/refund/status/confirm_secure_code/resend_otp/next_payment_step
These correspond to:
- Payment initiation (/pay)
- Payout (/payout)
- Refund (/refund)
- Transaction status (/status)
- 3DS confirmation (/confirm_secure_code)
- OTP confirmation (/resend_otp)
- Additional async steps in 3DS (/next_payment_step)
Configuration File
Routing for gateways is defined in config/gateways_routing.yml.
Callback Format
A callback request to our system must look like:
bash
curl --location 'http://business:4000/callbacks/v2/gateway_callbacks/<token>' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <jwt_token>' \
--data '{
"status": "approved",
"currency": "USD",
"amount": 100
}'
<token>= payment token- If status is
declined,reasonmust be provided - Required fields:
currency,amount
JWT Token Generation
- Header algorithm:
HS512 - Signing key: value of
sign_keyfrom gateway settings
Payload example:
json
{
"status": "approved",
"currency": "USD",
"amount": 100,
"secure": {
"encrypted_data": "UGcjWffaD4f1SHYMvdcO6qacI46LHIaqHnlbqCCpmwg=",
"iv_value": "JQwO5vk6DxXeMsva+FgUYA=="
}
}
Payment Requests (From ReactivePay)
Successful Response Fields:
- gateway_token (
String): External token / ID of the payment. - card_enrolled (
Boolean): Indicates if the card requires 3DS flow. Iftrue, the fieldredirect_requestmust be present. redirect_request (
Object): Required ifcard_enrolled = true. Consists of:- url (
String): Redirect or iframe processing URL. Iftype == post_iframes, this is alwayscheckout_requests/<payment_token>/processing. - type (
String): Determines the redirect logic. Possible values: post_iframes: Parameters are used to render and auto-submit a form inside an iframe.get_with_processing: Redirects to a processing endpoint and then toredirect_request.url.get: Direct redirect toredirect_request.url.post: Sends a POST request toredirect_request.url.redirect_html: Includes a raw ACS HTML form to be rendered after redirect.- wait_milliseconds (
Integer): Delay before marking the payment as processed and triggeringnext_payment_stepasynchronously if needed. - params (
Object): Form data (used in all types exceptpost_iframes). - iframes (
Object): Forpost_iframes, contains: - url: ACS backend URL.
- data: Parameters for fingerprinting and rendering the ACS form.
- url (
logs (
Arrayof Objects): Request/response logs.- request (
Object): The outgoing request to the provider. - response (
Object): The response received. - status (
String): HTTP status code. - created_at / updated_at (
Datetime): Timestamps of request lifecycle. - kind (
String): Name of the endpoint or request. - gateway (
String): Name of the integration. - duration (
Float): Request duration in seconds. - direction (
String):out(all external requests except callbacks),in(for callbacks).
- request (
Declined Requests:
- HTTP status: Anything but 200
- Error field:
- error (
String) - or errors (
Array<String>)
- error (
Example Payment Request
{
"params": {
"settings": {
"sandbox": true
},
"params": {
"pan": "4392963203551251",
"expires": "08/2025",
"holder": "John Doe",
"cvv": "196",
"email": "50-18@gmail.com",
"country": "AU",
"city": "Transmetropolitan",
"state": "AU",
"phone": "+77022579074",
"browser": {
"accept_header": "application/json, text/plain, */*",
"color_depth": "32",
"ip": "109.48.0.1",
"language": "us-US",
"screen_height": "1080",
"screen_width": "1920",
"tz": "-180",
"user_agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:92.0)",
"java_enabled": "true",
"window_width": "1240",
"window_height": "560"
},
"first_name": "Test",
"last_name": "Test2",
"extra_return_param": "_blank_"
},
"payment": {
"redirect_success_url": "https://success.foxew.com/?token=4gbk3dVf8QbdVp7KWCbKvHtgfXKtSWCk&type=pay&status=approved&extraReturnParam=_blank_&orderNumber=REPF72G59N&walletDisplayName=&amount=100000¤cy=EUR&gatewayAmount=100000&gatewayCurrency=EUR&cardHolder=John+Doe&sanitizedMask=439296******1251&walletToken=52f58064f38a53e390546b18c1bf2fcf1020&signature=1db8de71a1ed8d10a0240561ce7748e4",
"redirect_fail_url": "https://declined.foxew.com/?token=4gbk3dVf8QbdVp7KWCbKvHtgfXKtSWCk&type=pay&status=declined&extraReturnParam=_blank_&orderNumber=REPF72G59N&walletDisplayName=&amount=100000¤cy=EUR&gatewayAmount=100000&gatewayCurrency=EUR&cardHolder=John+Doe&sanitizedMask=439296******1251&walletToken=52f58064f38a53e390546b18c1bf2fcf1020&signature=1db8de71a1ed8d10a0240561ce7748e4",
"token": "4gbk3dVf8QbdVp7KWCbKvHtgfXKtSWCk",
"gateway_amount": 100000,
"gateway_currency": "EUR"
},
"processing_url": "http://business:4000/checkout_results/<token>/processing",
"callback_url": "http://business:4000/callbacks/v2/gateway_callbacks/<token>",
"callback_3ds_url": "http://business:4000/checkout_results/<token>/callback_3ds",
"method_name": "pay"
}
}
resultmust be eithersuccessordeclined- If
status: approvedis returned, the transaction is still not finalized until a callback is received or status is polled
Example 3DS Payment Response
{
"status": "ok",
"gateway_token": "4409b224-b1a7-41e5-8216-7c98560f4f08",
"result": "success",
"card_3ds_enrolled": true,
"processingUrl": "http://business:4000/checkout_results/<token>/processing",
"redirect_request": {
"url": "http://business:4000/checkout_results/<token>/processing",
"type": "post_iframes",
"iframes": [
{
"url": "http://test:9090/3ds",
"data": {
"creq": "<creq-token>",
"token": "<token>"
}
}
]
},
"logs": [
{
"gateway": "testing",
"request": {
"url": "https://some.site",
"params": {
"callback_url": "https://foxew.com",
"amount": 1234,
"currency": "USD",
"extra_return_param": "Tinkoff",
"order_number": "AutoTest#Payout-5264998"
}
},
"status": 200,
"response": "{status: \"OK\"}",
"kind": "request",
"created_at": "2025-05-15T18:21:55+02:00",
"duration": 0.5
}
]
}
Payout Request
Payout fields mostly mirror the payment request. Sample:
{
"callback_url": "https://foxew.com",
"amount": 1234,
"currency": "USD",
"extra_return_param": "Tinkoff",
"order_number": "AutoTest#Payout-5264998",
"card": {
"pan": "4392963203551251",
"expires": "03/2027",
"holder": "Test Test",
"cvv": "123"
},
"customer": {
"email": "test@test.com",
"phone": "79151002030",
"ip": "1.2.3.4"
},
"ip": "1.2.3.4",
"user_agent": "PostmanRuntime/7.43.0",
"remote_ip": "172.19.0.1"
}
Payout Success Response
{
"status": "ok",
"gateway_token": "f268ae4e-e5b4-451d-a46c-7c7b44d47195",
"result": "success",
"processingUrl": "http://business:4000/checkout_results/<token>/processing",
"logs": [...]
}
Payment Endpoints
| path | method | description |
|---|---|---|
| /pay | POST | Pay request |
| /status | POST | Check status |
| /payout | POST | Payout request |
| /refund | POST | Refund request |
| /confirm_secure_code | POST | Confirm 3ds or OTP secret key |
| /next_payment_step | POST | For second request for ACS or appruve 3ds (if necessary) |
- For P2P integrations
P2P Settings Overview
Flow of P2P Payments

Diagram of P2P Payment Flow

General Structure for P2P
According to the p2p payment flow, we are redirected to some page that the provider returns to us. Or it will be generated in the business logic of the integration. The settings parameter "wrapped_to_json_response" is responsible for make the response in JSON format, without redirects.
For payments parameters are wrapped using:
- Incoming request parameters
- Static settings defined per merchant's gateway
- Fields from the payments table in the database
Each payment method (pay, payout, refund, etc.) can have its own selector configuration.
Integration settings in /business/config/gateways_routing.yml define how the integration behaves, including which parameters to include in requests, whether to enable status checking, and other method-specific configurations.
Merchant settings in settings service define which gateways are available for each currency and how they are configured. This includes the class name, sandbox mode, and other parameters.
Example Structure of Integration Settings
default:
gateway_routes:
testpay:
full_link: "http://test:9090"
enable: true
callback: true
gateway_key: testpay
status_checker_time_rates:
"1-3": 30
"10-": 300
"4-6": 60
"7-9": 120
processing_method: http_requests
methods:
confirm_secure_code:
params_fields:
params:
- headers
- cres
- checkout_result_token
pay:
enable_status_checker: true
final_waiting_seconds: 15
params_fields:
callback_3ds_url: true
callback_url: true
params:
- pan
- expires
- holder
- cvv
- email
- country
- city
- state
- phone
- browser
- first_name
- last_name
- extra_return_param
payment:
- token
- gateway_token
- gateway_amount
- gateway_currency
processing_url: true
settings:
- sandbox
payout:
enable_status_checker: true
params_fields:
callback_url: true
params:
- pan
- expires
- holder
- cvv
- email
- country
- city
- state
- phone
- browser
- first_name
- last_name
- extra_return_param
payment:
- token
- gateway_token
- gateway_amount
- gateway_currency
processing_url: true
settings:
- sandbox
refund:
enable_status_checker: true
params_fields:
params:
- amount
payment:
- amount
- gateway_token
status:
params_fields:
params:
- token
payment:
- gateway_token
Additional Top-Level Settings
| Field | Type | Description |
|---|---|---|
class |
String | Integration class name, matches container or routing name (e.g., testpay) |
processing_method |
String | Communication method: http_requests or rabbit (currently only HTTP) |
status_checker_time_rates |
Object | Polling intervals per retry group |
full_link |
String | Full URL of the gateway including protocol, host, and port |
enable |
Boolean | Indicates if the integration is enabled on the platform |
methods |
Object | Configuration for available methods and their parameters |
callback |
Boolean | Indicates if the gateway supports callbacks (default: true) |
gateway_key |
String | Unique key for the gateway, used for routing and identification(e.g., testpay, is equal to class in merchant settings) |
Example Method Configuration
| Field | Type | Description |
|---|---|---|
final_waiting_seconds |
Integer | Seconds before initiating status check (default: 15) |
enable_status_checker |
Boolean | Enable payment status polling (default: true) |
params_fields |
Object | Configuration for request parameters |
params_fields.callback_url |
Boolean | Generic callback URL (default: true) |
params_fields.processing_url |
Boolean | Final redirect URL (default: true) |
params_fields.params |
Array | Request parameters (see below) |
params_fields.payment |
Array | Allowed fields from the payments record (see below) |
params_fields.settings |
Array | Static config flags (see below) |
params_fields.params.cvv |
String | CVV code (default: true) |
params_fields.params.email |
String | Customer email (default: true) |
params_fields.params.country |
String | Customer country (default: true) |
params_fields.params.city |
String | Customer city (default: true) |
params_fields.params.state |
String | Customer state (default: true) |
params_fields.params.phone |
String | Customer phone (default: true) |
params_fields.params.browser |
String | Browser data (JSON object) (default: true) |
params_fields.params.first_name |
String | Customer first name (default: true) |
params_fields.params.last_name |
String | Customer last name (default: true) |
params_fields.params.extra_return_param |
String | Extra return parameter (default: true) |
params_fields.payment.token |
String | Internal payment token (default: true) |
params_fields.payment.gateway_token |
String | External gateway token (default: true) |
params_fields.payment.gateway_amount |
String | Converted amount (default: true) |
params_fields.payment.gateway_currency |
String | Converted currency (default: true) |
params_fields.settings.sandbox |
Boolean | Enable sandbox (test) mode (default: true) |
pay:
enable_status_checker: true
final_waiting_seconds: 15
params_fields:
callback_3ds_url: true
callback_url: true
params:
- pan
- expires
- holder
- cvv
- email
- country
- city
- state
- phone
- browser
- first_name
- last_name
- extra_return_param
payment:
- token
- gateway_token
- gateway_amount
- gateway_currency
processing_url: true
settings:
- sandbox
Available parameter fields
| Field | Type | Description |
|---|---|---|
email |
String | Customer email |
country |
String | Customer country |
city |
String | Customer city |
state |
String | Customer state |
postcode |
String | Customer postal code |
street |
String | Customer street address |
address |
String | Customer address |
region |
String | Customer region |
phone |
String | Customer phone number |
otp |
String | One-time password |
pin |
String | Personal identification number |
bank_code |
String | Bank code |
bank_account_number |
String | Bank account number |
identify_type |
String | Identification type |
identify_number |
String | Identification number |
recurring |
Boolean | Recurring payment flag |
need_confirmation |
Boolean | Confirmation requirement |
browser |
Object | Browser data (JSON object) |
country_code |
String | Customer country code |
first_name |
String | Customer first name |
father_name |
String | Customer father's name |
last_name |
String | Customer last name |
website |
String | Customer website |
birthday |
String | Customer birthday |
state |
String | Customer state |
recurring_data |
Object | Recurring payment data |
pending_url |
String | URL for pending payments |
extra_return_param |
String | Extra return parameter |
Available payment fields
| Field | Type | Description |
|---|---|---|
id |
Integer | Payment ID (we don't use it field) |
status |
String | Payment status |
token |
String | Internal payment token |
currency |
String | Origin payment currency |
product |
String | Product name |
callback_url |
String | Merchant callback URL |
redirect_success_url |
String | URL for successful payment redirect |
redirect_fail_url |
String | URL for failed payment redirect |
redirect_request |
String | URL for redirect request (non used) |
merchant_private_key |
String | Private key for Merchant record |
amount |
Integer | Payment amount |
gatewayable_type |
String | Name of gateway (e.g., `Gateway::TestpayPayment) |
gatewayable_id |
Integer | ID of gateway record |
created_at |
Datetime | Payment creation date |
updated_at |
Datetime | Payment update date |
extra_return_param |
String | Extra return parameter |
operation_type |
String | Payment operation type (e.g., pay, refund, payout) |
order_number |
String | Merchant order number |
declination_reason |
String | Reason for payment declination |
lead_id |
Integer | ID of the lead record |
ip |
String | Customer IP address |
browser_info |
String | Browser information |
kind |
String | Payment kind (e.g., wallet, direct) |
refund_id |
Integer | ID of the refund payment record |
scoring_remark |
String | Scoring remark |
country_code_by_BIN |
String | Country code by BIN (e.g., US) |
country_code_by_phone |
String | Country code by phone (e.g., US) |
country_code_by_IP |
String | Country code by IP (e.g., US) |
business_account_legal_name |
String | Business account legal name from merchant core settings |
business_account_profileID |
String | Business account profile ID from merchant core settings |
gateway_details |
JSON | Gateway-specific details (containts gateway_ids, redirect_request and other data`) |
gateway_currency |
String | Gateway currency after conversion |
gateway_amount |
Integer | Gateway amount after conversion |
details |
JSON | Additional details (e.g., request data, error data etc.) |
scoring_action |
String | Scoring action (e.g., success) |
scoring_action_log |
JSON | Scoring action log (e.g., {riskscore: {...}}) |
gateway_alias |
String | Gateway alias (e.g., testpay) |
routing_logs |
JSON | Routing logs (e.g., {prev_route: null {next_route...}}) |
gateway_token |
String | External gateway ID (e.g., 123456789) |
two_stage_mode |
Boolean | Indicates if the payment is in two-stage mode |
settings |
JSON | Payment settings (e.g., {status_checker_time_rates: {...}}) |
rrn |
String | RRN (Retrieval Reference Number) |
commission_data |
JSON | Commission data (e.g., {commission_fee: 10, commission_amount: 10}) |
notification_settings |
JSON | Notification settings (e.g., {"recipient"=>"test@test.tt", "allow_notification"=>true}) |
customer_country |
String | Customer country (e.g., US) |
trader_id |
Integer | Trader ID (e.g., 123456) |
merchant_url |
String | Merchant URL (e.g., http://example.com) |
reference |
String | Reference (e.g., 123456789) |
locale |
String | Locale (e.g., en-US) |
Allowed Fields (field "method")
| Field | Type | Description |
|---|---|---|
pay |
String | Payment initiatiate |
status |
String | Payment status check (if enable_status_checker is true) |
confirm_secure_code |
String | OTP confirmation |
payout |
String | Outgoing payout |
refund |
String | Refund payment |
resend_otp |
String | Resend OTP code |
decline |
String | Decline payment |
For settings methods need to be set in the methods section of the configuration file.
methods:
confirm_secure_code:
params_fields:
params:
- headers
- cres
- checkout_result_token
...
pay:
enable_status_checker: true
final_waiting_seconds: 15
params_fields:
callback_3ds_url: true
callback_url: true
params:
- pan
...
payout:
enable_status_checker: true
params_fields:
callback_url: true
params:
- pan
- expires
...etc.
Example Status Checker Time Rates
| status_checker_time_rate's count ranges | Interval (seconds) | Description |
|---|---|---|
1-3 |
30 | Every 30s for attempts 1 to 3 |
4-6 |
60 | Every 60s for attempts 4 to 6 |
7-9 |
120 | Every 120s for attempts 7 to 9 |
10- |
300 | Every 300s (5 min) for 10th attempt and later |
status_checker_time_rates:
"1-3": 30
"10-": 300
"4-6": 60
"7-9": 120
This configuration controls how often the status of a payment is re-checked after creation.
Each setting allows you to tailor integration behavior for different gateway providers and payment types. These configurations ensure correct data handling, structured requests, and environment-specific behavior.
Gateway Routing Configuration
There describes the structure and purpose of the gateways_routing.yml configuration file. This file contains network-related settings for available integrations and their enabled status.
Configuration Location
/business/config/gateways_routing.yml
Field Descriptions
| Field | Type | Description |
|---|---|---|
decline_payout_on_request_error |
Boolean | Decline payout if request has server or client fails (default: true) |
class |
String | Integration class name, matches container or routing name (e.g., testpay and it is qual gateway_key) |
wrapped_to_json_response |
Boolean | If true, the response is wrapped in JSON format without redirects (default: false) |
sandbox |
Boolean | Indicates whether gateway is in test/sandbox mode |
sign_key |
String | Secret used to sign callback JWTs |
These settings are environment-agnostic and loaded for all environments by default unless overridden.
Example structure of Merchant Settings
{
"EUR": {
"gateways": {
"pay": {
"default": "testpay"
},
"payout": {
"providers": [
{
"default": "testpay_p2p"
}
]
}
}
},
"USD": {
"gateways": {
"pay": {
"providers": [
{
"DDDDD": "testpay_p2p"
}
]
},
"payout": {
"default": "testpay_p2p"
}
}
},
"gateways": {
"allow_host2host": true,
"testpay": {
"class": "testpay",
"sandbox": true,
"sign_key": "efc63a792a8375213898a166e50bc64d"
},
"testpay_p2p": {
"class": "testpay_p2p",
"decline_payout_on_request_error": true,
"sandbox": true,
"sign_key": "22263a792a8375213898a166e50bc64d",
"wrapped_to_json_response": false
}
}
}
ReactivePay P2P Payment Request and Response Documentation
Accepted Fields in P2P Payment Request
Section: customer
ip(String): Client's IP addressphone(String): Client's phone numbername(String): Client's full nameemail(String): Client's email addressclient_id(String): Client's internal IDfirst_name(String): Client's first namelast_name(String): Client's last namemiddle_name(String): Client's middle nameaddress(String): Client's addresscity(String): Client's citystate(String): Client's state or regionpostcode(String): Client's postal codecountry(String): Client's countrylocale(String): Client's language localeuserid(String): Client's user ID in the merchant systemmode_of_payment(String): Mode of paymentcoin(String): Currencypayment_system_id(String): ID of the payment systemcard_bin(String): BIN of the cardbirthday(String): Client's date of birthbrowser(Object): Browser options object
Section: bank_account
bank_account(String): Bank account numberaccount_holder(String): Account holder's namebank_state(String): Bank's statebank_city(String): Bank's cityifsc_code(String): IFSC code (India specific)bank_address(String): Bank's addressaccount_number(String): Account numberaccount_name(String): Account holder namerequisite_type(String): Type of requisitebank_name(String): Bank name
Section: default
recurring(Boolean): Indicates if the payment is recurringwebsite(String): Website of the merchantextra_return_param(String): Additional return parameterspending_url(String): Immediate redirect URL for waiting pageneed_confirmation(Boolean): Indicates two-step payment requirement
Server Response for P2P Payments
redirect_request(Object): Contains the URL to redirect the user to the P2P payment page. Usually excluded whenwrapped_to_jsonis enabled in settings.requisites(Object): Contains trader's requisites, displayed on the form or returned in JSON.provider_response_data(Object): Contains detailed info from the provider.support_banks(Array): List of supported bankssupport_bank_native(String): Preferred or native bankname_seller(String): Seller’s name (cardholder or account owner)pan(String): PAN value – could be a card number or phone number for P2P
logs(Array): Logs of the operation. Refer to the standard logs structure.
Payment Endpoints
| path | method | description |
|---|---|---|
| /pay | POST | Pay request |
| /status | POST | Check status |
| /payout | POST | Payout request |
| /refund | POST | Refund request |
| /confirm_secure_code | POST | Confirm OTP secret key |
| /p2p_redirect | GET | Redirect to charge page |
| /pay_by_form | POST | Approve payment by form |
ReactivePay Callback Integration Manual
ReactivePay receives asynchronous notifications (callbacks) about payment events. These callbacks must be authenticated, validated, and follow specific payload and security requirements.
2. Callback Endpoint
Endpoint format:
POST /callbacks/v2/gateway_callbacks/:token
Where :token is the Payment.token of the current transaction.
3. Authentication & JWT Format
Callbacks are authenticated using a JWT token in the Authorization header:
Authorization: Bearer <JWT_TOKEN>
JWT Header:
{
"alg": "HS512"
}
JWT Payload:
{
"status": "approved",
"currency": "USD",
"amount": 100,
"secure": {
"encrypted_data": "<base64-string>",
"iv_value": "<base64-string>"
}
}
securecontains the encryptedmerchant_key(AES-256-CBC).HS512signature usessettings['sign_key']as the secret.
4. Encrypting the Merchant Key
Use AES-256-CBC encryption with the system master key:
Ruby Reference:
Code: Copy
openssl enc -aes-256-cbc -in merchant_key.txt -out encrypted_key.bin -K <master_token> -iv <iv_value>
<?php
function encryptMerchantKey(string $merchantKey, string $masterToken): array {
// Cut the master token to 32 bytes
$key = substr($masterToken, 0, 32);
$iv = openssl_random_pseudo_bytes(16); // 16 bytes for AES-CBC IV
$encryptedData = openssl_encrypt(
$merchantKey,
'aes-256-cbc',
$key,
OPENSSL_RAW_DATA,
$iv
);
return [
'encrypted_data' => base64_encode($encryptedData),
'iv_value' => base64_encode($iv),
];
}
// Example usage
$merchantKey = 'merchant_private_key_string';
$masterToken = 'your_very_long_32+_byte_master_token_here';
$result = encryptMerchantKey($merchantKey, $masterToken);
print_r($result);
import base64
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
def pad(data):
pad_len = 16 - (len(data) % 16)
return data + bytes([pad_len] * pad_len)
def encrypt_merchant_key(merchant_key: str, master_token: str):
key = master_token.encode('utf-8')[:32]
iv = get_random_bytes(16)
cipher = AES.new(key, AES.MODE_CBC, iv)
padded_data = pad(merchant_key.encode('utf-8'))
encrypted = cipher.encrypt(padded_data)
return {
"encrypted_data": base64.b64encode(encrypted).decode('utf-8'),
"iv_value": base64.b64encode(iv).decode('utf-8')
}
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.security.SecureRandom;
public class AesEncryptor {
public static void main(String[] args) throws Exception {
String merchantKey = "merchant_private_key_string";
String masterToken = "your_very_long_32+_byte_master_token_here";
// Cut the master token to 32 bytes
byte[] keyBytes = masterToken.substring(0, 32).getBytes("UTF-8");
// Generate random IV
byte[] ivBytes = new byte[16];
new SecureRandom().nextBytes(ivBytes);
// Encrypt the merchant key
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] encrypted = cipher.doFinal(merchantKey.getBytes("UTF-8"));
// Encode to Base64
String encryptedData = Base64.getEncoder().encodeToString(encrypted);
String ivValue = Base64.getEncoder().encodeToString(ivBytes);
System.out.println("encrypted_data: " + encryptedData);
System.out.println("iv_value: " + ivValue);
}
}
Output(#)
{
"encrypted_data": "<Base64>",
"iv_value": "<Base64>"
}
See language-specific examples in callback_request_manual.md.
5. Payload Schema Validation
Each request must conform to the following JSON schema:
{
"type": "object",
"properties": {
"token": {
"type": "string",
"minLength": 32,
"maxLength": 32
},
"gateway_token": {
"type": "string"
},
"status": {
"type": "string",
"pattern": "^(approved|declined|refunded)$"
},
"refund": {
"type": "boolean"
},
"reason": {
"type": "string",
"minLength": 5
},
"currency": {
"type": "string",
"minLength": 3,
"maxLength": 4
},
"amount": {
"type": "integer"
},
"gateway_callback": {
"type": "object"
}
},
"required": ["token", "status", "currency", "amount"]
}
6. Server-side Validation Steps
a. Extract and decode JWT:
function extractSecureBlock(jwt_token):
base64_payload = jwt_token.split('.')[1]
decoded_payload = Base64Decode(base64_payload)
json_payload = ParseJSON(decoded_payload)
secure_block = json_payload["secure"]
return secure_block
b. Decrypt merchant key:
function decryptMerchantKey(secure_block):
encrypted_data = secure_block["encrypted_data"]
iv = secure_block["iv_value"]
master_key = GetConfig("master_token")[0:32]
merchant_private_key = AES256Decrypt(
key = master_key,
iv = iv,
data = encrypted_data
)
return merchant_private_key
c. Validate JWT signature:
function validateJwtSignature(jwt_token, sign_key):
is_valid = JwtVerify(token = jwt_token, secret = sign_key, algorithm = "HS512")
return is_valid
7. Success and Error Handling
Success:
- HTTP 200
- Response:
{ success: true }
Errors:
| Code | Message |
|---|---|
| 401 | Authorization token is missing |
| 422 | Invalid JWT signature |
| 422 | Invalid payload schema |
8. Example CURL Request
curl --location 'http://business:4000/callbacks/v2/gateway_callbacks/gJJN4VytdaNxivS5kcvWkeMb448EWJQc' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <JWT_TOKEN>' \
--data '{
"status": "approved",
"currency": "USD",
"amount": 100
}'
9. Logs and Debugging
Each callback is logged via InteractionLogger with:
- request (raw & parsed)
- response
- direction: "in"
- timestamps and duration
10. Common Pitfalls
- Incorrect master token for AES key
- Missing fields in secure payload
- Expired or mismatched JWT tokens
- Schema violations (e.g. short token, missing currency)