Overview
Base URL: https://{gateway-host}/api/pay
All API requests use the POST method. The request and response body are JSON format {"payload": "<AES encrypted Base64 string>"}. Communication is secured by AES encryption + RSA signature.
All JSON field names use lower camel case. Enum values must be sent exactly as documented, for example CREDIT_CARD and LOCAL.
Security Protocol
Keys
| Key Type | Description |
|---|---|
| Merchant AES Key | Each merchant certificate has its own 128-bit Base64 key for request and response payload encryption. |
| Merchant RSA Key Pair | 2048-bit RSA. The merchant keeps the private key and configures the public key with its matching AES key on the platform. |
| Platform RSA Key Pair | 2048-bit RSA. Platform keeps the private key, provides the public key to the merchant. Used to sign responses. |
Request Format
Headers (all required)
| Header | Description | Example |
|---|---|---|
merchantNo | Merchant number | M20240101001 |
version | API version | 1.0 |
requestId | Unique request ID (idempotency) | req_abc123 |
timestamp | Current time in ms (within 10 s, not future) | 1700000000000 |
sign | RSA signature | Base64 string |
Content-Type | Fixed | application/json |
Body
The request body is JSON format. The encrypted ciphertext is placed in the payload field.
{"payload": "<AES encrypted Base64 string>"}
Encryption Process
- Serialize business parameters to JSON string (the "plaintext")
- AES encrypt the plaintext → encrypted string (Base64)
- Concatenate sign string:
merchantNo|version|requestId|timestamp|<encrypted string> - RSA sign with merchant private key (SHA256withRSA) →
sign(Base64) - Send request, body is
{"payload": "<encrypted string>"}
AES Details
| Algorithm | AES/CBC/PKCS5Padding |
| Key | 128-bit, Base64 encoded |
| IV | First 16 bytes of the AES key |
Response Format
Headers
| Header | Description |
|---|---|
merchantNo | Echo merchant number |
version | API version |
requestId | Echo request ID |
timestamp | Response time in ms |
code | Response code (see below) |
message | Response message |
sign | Platform RSA signature |
Body
The response body is JSON format. The encrypted ciphertext is in the payload field. Empty string if the request failed.
{"payload": "<AES encrypted Base64 string>"}
Decryption Process
- Extract the
payloadvalue from the response body JSON - Verify signature: concatenate
merchantNo|version|requestId|timestamp|code|message|<payload value>, verify with platform public key - If
codeis200, AES decrypt the payload value to get business response JSON - If
codeis not200, the request failed — checkcodeandmessage
Response Codes
| Code | Message | Description |
|---|---|---|
200 | succeed | Success |
400 | payload decrypt failed | AES decryption failed |
401 | merchant not supported | Merchant not found, disabled, or keys not configured |
403 | the ip is not whitelisted | IP not in whitelist |
407 | verify sign failed | RSA signature verification failed |
417 | parameter is null or invalid | Missing or invalid parameter (includes timestamp expired) |
500 | system error | Internal server error |
All date-time fields in merchant API responses use yyyy-MM-dd HH:mm:ss. Date-only fields use yyyy-MM-dd, and time-only fields use HH:mm:ss.
Card Acquiring Products and Flows
Card acquiring supports hosted checkout and API integration modes.
| Mode | Order Flow |
|---|---|
| Checkout | Create order → redirect to checkoutUrl → receive notification |
| API | Query dynamic fields → create order → complete returned action → receive notification |
Query Dynamic Required Fields
POST/api/pay/required-fields
Queries extension fields required for the selected payment method before order creation. Checkout mode returns an empty array.
Request Parameters
| Field | Type | Required | Description |
|---|---|---|---|
action | string | Yes | PAY or PAYOUT |
currency | string | Yes | ISO 4217 currency code |
amount | number | Yes | Transaction amount, greater than zero |
payType | string | Yes | Payment method, for example CARD or PIX |
integrationMode | string | Pay-in only | HOSTED or DIRECT |
context | object | No | Mode and query conditions; see example |
context Fields
| Field | Type | Required When | Values / Description |
|---|---|---|---|
payoutMode | string | Local payout | Fixed value LOCAL |
supplier | string | Depends on local payout method | Service type, for example PIX |
Response Fields
| Field | Type | Description |
|---|---|---|
action | string | Requested transaction direction |
currency | string | Transaction currency |
payType | string | Payment method |
schemaVersion | string | Field definition version |
fields | array/object | Field definitions to submit in order extra |
{
"action": "PAY",
"currency": "USD",
"amount": 100,
"payType": "CREDIT_CARD",
"integrationMode": "DIRECT"
}
{
"action": "PAY",
"currency": "USD",
"payType": "CREDIT_CARD",
"integrationMode": "DIRECT",
"schemaVersion": "54c29e37dbf9431a",
"fields": [
{"fieldName":"senderCardNumber","path":"cardInfo.number","required":true},
{"fieldName":"senderFirstName","path":"extra.senderFirstName","required":true}
]
}
integrationMode is provider-neutral. Pay-in defaults to HOSTED; query required fields before creating a DIRECT order.
Items in fields
| Field | Type | Description |
|---|---|---|
fieldName | string | Field name to submit in order extra |
path | string | Complete path in the create-order request; use this value as authoritative |
required | boolean | Whether the field is required |
Merchants should rely on path and required. Put cardInfo.number in top-level cardInfo, customerEmail at the top level, and extra.senderFirstName in extra. If several required fields are absent, create-order returns all missing paths in missingFields.
Create Card Payment
POST/api/pay/in
Creates a collection (pay-in) order and returns a payment URL.
Request Parameters (plaintext JSON before encryption)
| Field | Type | Required | Description |
|---|---|---|---|
userId | string | Yes | Merchant-side unique user identifier |
outTradeNo | string | Yes | Merchant's unique order number |
amount | number | Yes | Payment amount |
currency | string | Yes | Currency code (see enum below) |
payType | string | Yes | Payment type (see enum below) |
integrationMode | string | No | HOSTED (default) or DIRECT |
notifyUrl | string | No | Async callback URL for payment result |
returnUrl | string | No | Redirect URL after payment |
customerName | string | No | Customer name |
customerPhone | string | No | Customer phone |
customerEmail | string | No | Customer email |
customerIp | string | No | Client IP |
cardInfo | object | No | Card information (see below) |
extra | object | No | Payment-method extension fields |
Card Acquiring Mode Parameters
| Mode | Required Parameters | Availability |
|---|---|---|
| Hosted | integrationMode=HOSTED, payType, customerIp, and returned required fields | Available |
| Direct | integrationMode=DIRECT; query required fields and submit them by path | Available |
integrationMode is a top-level platform-standard field: HOSTED (default) or DIRECT.
Pay-in extra Fields
| Field | Type | Required When | Description |
|---|---|---|---|
country | string | Checkout | Customer country/region |
city / state / address / zip | string | No | Address details |
dateOfBirth | string | No | Date of birth |
cancelUrl | string | No | Cancellation redirect URL |
lang / websiteUrl | string | No | Checkout display information |
embedParentOrigin | string | Embedded checkout | Parent page Origin |
appPlatform / appBundleId | string | Mobile app integration | ios / android and the application bundle ID |
senderPaymentReference / riskToken | string | Depends on API method | Reference and risk token |
userAgent / acceptHeader / language | string | Depends on API method | Browser information |
colorDepth / screenHeight / screenWidth / timeZone | number | Depends on API method | Browser color depth, screen dimensions, and UTC offset in minutes |
javaEnabled / javaScriptEnabled | boolean | Depends on API method | Browser capabilities |
countryOfBillingAddress | string | Depends on API method | Billing country/region |
For API mode, submit card data in top-level cardInfo. Submit other dynamic fields in extra using the exact names returned by Query Required Fields.
actionType is not a create-order request field. The platform returns it in the response: REDIRECT means the payer must open payUrl, while NOMORE means no additional action is required.
cardInfo Object
| Field | Type | Required | Description |
|---|---|---|---|
number | string | No | Card number |
expireMonth | string | No | Expiration month (e.g. "01") |
expireYear | string | No | Expiration year (e.g. "2026") |
cvv | string | No | CVV / CVC security code |
token | string | No | Card token (for tokenized cards) |
name | string | No | Cardholder name |
payType Enum
| Value | Description |
|---|---|
CREDIT_CARD | Credit card payment |
currency Enum
| Value | Description |
|---|---|
EUR | Euro |
Response (decrypted payload)
| Field | Type | Description |
|---|---|---|
tradeId | string | Platform trade ID |
outTradeNo | string | Merchant order number (echo) |
tradeStatus | string | PAYING / SUCCESS / FAIL |
tradeAmount / merchantAmount | number | Order and merchant settlement amounts |
currency | string | Currency code |
checkoutUrl | string | Platform checkout URL (recommended) |
payUrl | string | Original checkout URL; returned when enabled |
errMsg | string | Merchant-safe failure reason |
The response also includes userId, payType, customer details, notification/redirect URLs, and timestamps. Its structure matches Pay-in Query and pay-in callbacks.
Example
{
"userId": "USER10001",
"outTradeNo": "ORD20240101001",
"amount": 500.00,
"currency": "EUR",
"payType": "CREDIT_CARD",
"notifyUrl": "https://merchant.com/notify",
"returnUrl": "https://merchant.com/return",
"customerName": "John",
"customerPhone": "9876543210",
"customerEmail": "[email protected]",
"cardInfo": {
"number": "4111111111111111",
"expireMonth": "12",
"expireYear": "2026",
"cvv": "123",
"name": "John"
}
}{
"tradeId": "T20240101120000001",
"outTradeNo": "ORD20240101001",
"tradeStatus": "PAYING",
"tradeAmount": 500.00,
"currency": "EUR",
"checkoutUrl": "https://pay.example.com/checkout?token=opaque-token"
}Query Card Payment
POST/api/pay/in/query
Query a pay-in order by tradeId or outTradeNo.
Request Parameters
| Field | Type | Required | Description |
|---|---|---|---|
tradeId | string | Conditional | Platform trade ID (one of two required) |
outTradeNo | string | Conditional | Merchant order number (one of two required) |
Response
| Field | Type | Description |
|---|---|---|
tradeId | string | Platform trade ID |
outTradeNo | string | Merchant order number |
tradeStatus | string | Order status (see below) |
tradeAmount | number | Order amount |
merchantAmount | number | Merchant settlement amount (after fee) |
currency | string | Currency code |
userId | string | Merchant customer identifier |
payType | string | Payment method |
customerName / customerPhone / customerEmail | string | Customer details |
customerIp | string | Customer IP address |
notifyUrl | string | Async notification URL |
returnUrl | string | Redirect URL after payment |
payUrl | string | Original payment URL, when enabled |
checkoutUrl | string | Platform checkout URL |
createTime / tradeCompleteTime | string | Creation and completion times |
errMsg | string | Merchant-safe failure reason |
Order Status Values
| Status | Description |
|---|---|
| PAYING | Payment in progress |
| SUCCESS | Payment successful |
| FAIL | Payment failed |
Example
{
"outTradeNo": "ORD20240101001"
}{
"tradeId": "T20240101120000001",
"outTradeNo": "ORD20240101001",
"tradeStatus": "SUCCESS",
"tradeAmount": 500.00,
"merchantAmount": 485.00,
"currency": "INR",
"checkoutUrl": "https://pay.example.com/checkout?token=opaque-token"
}Query Local Payout
POST/api/pay/out/query
Query a payout order by tradeId or outTradeNo.
Request Parameters
| Field | Type | Required | Description |
|---|---|---|---|
tradeId | string | Conditional | Platform trade ID (one of two required) |
outTradeNo | string | Conditional | Merchant order number (one of two required) |
Response Parameters
| Field | Type | Description |
|---|---|---|
tradeId | string | Platform trade ID |
outTradeNo | string | Merchant order number |
tradeStatus | string | Order status |
tradeAmount | number | Order amount |
merchantAmount | number | Merchant settlement amount |
feeAmount | number | Merchant fee |
debitAmount | number | Total debited amount including the fee |
currency | string | Currency code |
userId | string | Merchant customer identifier |
payType | string | Payout method |
customerName | string | Customer name |
customerPhone | string | Customer phone number |
customerEmail | string | Customer email |
customerIp | string | Customer IP address |
notifyUrl | string | Async notification URL |
utr | string | Transaction reference after success |
createTime | string | Order creation time |
tradeCompleteTime | string | Order completion time |
errMsg | string | Merchant-safe failure reason |
Order Status Values
| Status | Description |
|---|---|
| PAYING | Payment in progress |
| SUCCESS | Payment successful |
| FAIL | Payment failed |
Example
{
"tradeId": "T20240101120000002"
}{
"tradeId": "T20240101120000002",
"outTradeNo": "PAYOUT20240101001",
"tradeStatus": "SUCCESS",
"tradeAmount": 500.00,
"merchantAmount": 485.00,
"feeAmount": 15.00,
"debitAmount": 515.00,
"currency": "INR",
"utr": "UTR202401010001"
}Query Account Balance
POST/api/pay/balance
Query the merchant's account balances. Optionally filter by currency.
Request Parameters
| Field | Type | Required | Description |
|---|---|---|---|
currency | string | No | Currency code. If omitted, returns all currencies. |
Response
| Field | Type | Description |
|---|---|---|
collection | array | Pay-in account balances (funds received from customers) |
payment | array | Pay-out account balances (funds for disbursement) |
Each item in the array:
| Field | Type | Description |
|---|---|---|
currency | string | Currency code |
totalAmount | number | Total balance |
frozenAmount | number | Frozen amount |
availableAmount | number | Available balance |
Example
{
"currency": "INR"
}{
"collection": [
{
"currency": "INR",
"totalAmount": 100000.00,
"frozenAmount": 5000.00,
"availableAmount": 95000.00
}
],
"payment": [
{
"currency": "INR",
"totalAmount": 20000.00,
"frozenAmount": 0.00,
"availableAmount": 20000.00
}
]
}// Request
{}
// Response
{
"collection": [
{ "currency": "INR", "totalAmount": 100000.00, "frozenAmount": 5000.00, "availableAmount": 95000.00 },
{ "currency": "USDT", "totalAmount": 5000.00, "frozenAmount": 0.00, "availableAmount": 5000.00 }
],
"payment": []
}Create Local Payout
POST/api/pay/out
Creates a payout order and returns the platform order ID and current status.
| Field | Type | Required | Description |
|---|---|---|---|
userId | string | Yes | Merchant-side unique user identifier |
outTradeNo | string | Yes | Unique merchant order number |
amount | number | Yes | Amount greater than zero |
currency | string | Yes | ISO 4217 currency code |
notifyUrl | string | No | Asynchronous result URL |
customerName / customerPhone / customerEmail | string | No | Beneficiary details |
bankAccount / bankCardNo | string | Conditional | At least one payout destination is required |
bankCode / bankName | string | No | Bank details |
extra | object | No | Payment-method fields returned by Query Required Fields |
Payout extra Fields
| Field | Type | Required When | Description |
|---|---|---|---|
payoutMode | string | Yes | Fixed value LOCAL |
paymentType | string | Local payout | Payment method, for example PIX |
supplier | string | Depends on method | Service type |
accountNumber | string | Depends on method | Receiving account |
pixKeyType / pixKeyValue | string | As returned for PIX | PIX Key type and value |
idCard / nationalId / address | string | As returned | Beneficiary identity and address |
Requirements vary by country, payment method, and supplier. Query dynamic required fields before order creation and submit every returned fieldName in extra.
Response Fields
| Field | Type | Description |
|---|---|---|
tradeId | string | Platform trade ID |
outTradeNo | string | Merchant order number |
tradeStatus | string | PAYING or FAIL |
errMsg | string | Failure reason; omitted on success |
tradeAmount / merchantAmount / feeAmount / debitAmount | number | Order, settlement, fee, and debit amounts |
currency | string | Currency code |
utr | string | Transaction reference after success |
The response also includes userId, payType, customer details, notification URL, and timestamps. Its structure matches Payout Query and payout callbacks.
{"tradeId":"T202608070001","outTradeNo":"PAYOUT20260807001","tradeStatus":"PAYING"}
Create Refund
POST/api/pay/refund
Creates a refund request for an original pay-in order.
| Field | Type | Required | Description |
|---|---|---|---|
tradeId / outTradeNo | string | One required | Original pay-in identifier |
outRefundNo | string | Yes | Unique merchant refund number |
amount | number | Yes | Amount greater than zero |
reason | string | No | Refund reason |
notifyUrl | string | No | Refund result URL |
The platform first verifies that the original payment succeeded, cumulative refunds do not exceed the original amount, and both the channel adapter and account support refunds. No refund order is created when refunds are unsupported.
Response Fields
| Field | Type | Description |
|---|---|---|
refundId | string | Platform refund ID |
outRefundNo | string | Merchant refund number |
tradeId / outTradeNo | string | Original payment identifiers |
refundStatus | string | PAYING / SUCCESS / FAIL |
refundAmount | number | Refund amount |
currency | string | Original payment currency |
notifyUrl / reason | string | Notification URL and reason |
errMsg | string | Failure reason |
createTime / refundCompleteTime | string | Creation and completion times |
Query Refund
POST/api/pay/refund/query
| Field | Type | Required | Description |
|---|---|---|---|
tradeId | string | Conditional | Returns all refunds for the original platform trade ID |
outTradeNo | string | Conditional | Returns all refunds for the original merchant order number |
refundId | string | Conditional | Returns the matching platform refund |
outRefundNo | string | Conditional | Returns the matching merchant refund |
Exactly one query field must be provided. Response data is always an array. Original-payment queries return an empty array when no refunds exist; refund-ID queries return a single-element array.
Refund Notification
Sent when a refund reaches SUCCESS / FAIL. event is always TRADE_REFUND_ORDER_CALLBACK.
| Field | Type | Description |
|---|---|---|
event | string | Always TRADE_REFUND_ORDER_CALLBACK |
refundId / outRefundNo | string | Platform and merchant refund identifiers |
tradeId / outTradeNo | string | Original payment identifiers |
refundStatus | string | SUCCESS or FAIL |
refundAmount | number | Refund amount |
currency | string | Currency |
notifyUrl / reason / errMsg | string | Notification URL, reason, and failure reason |
createTime / refundCompleteTime | string | Creation and completion times |
Failed notifications are retried with increasing delays. Respond with plain text SUCCESS.
Card Payment Notification
When a pay-in reaches SUCCESS / FAIL, the platform sends a POST notification to its notifyUrl. event is always TRADE_IN_ORDER_CALLBACK.
Notification Parameters
| Field | Type | Description |
|---|---|---|
event | string | Always TRADE_IN_ORDER_CALLBACK |
tradeId | string | Platform trade ID |
outTradeNo | string | Merchant order number |
tradeStatus | string | SUCCESS or FAIL |
tradeAmount | number | Order amount |
merchantAmount | number | Merchant settlement amount |
currency | string | Currency code |
userId | string | Merchant customer identifier |
payType | string | Payment method |
customerName | string | Customer name |
customerPhone | string | Customer phone number |
customerEmail | string | Customer email |
customerIp | string | Customer IP address |
notifyUrl | string | Notification URL |
returnUrl | string | Redirect URL after payment |
actionType | string | Next action: REDIRECT or NOMORE |
payUrl | string | Original payment URL, when enabled |
checkoutUrl | string | Platform checkout URL |
createTime | string | Creation time |
tradeCompleteTime | string | Completion time |
errMsg | string | Failure reason, returned only on failure |
Rules
- Failed notifications are retried with increasing delays.
- Respond with plain text
SUCCESS(case-insensitive). - Any other response or timeout (5 seconds) triggers a retry.
Example
{
"event": "TRADE_IN_ORDER_CALLBACK",
"tradeId": "T20240101120000001",
"outTradeNo": "ORD20240101001",
"tradeStatus": "SUCCESS",
"tradeAmount": 500.00,
"merchantAmount": 485.00,
"currency": "INR",
"checkoutUrl": "https://pay.example.com/checkout?token=opaque-token"
}Merchant response: SUCCESS
Local Payout Notification
When a payout reaches SUCCESS / FAIL, the platform sends a POST notification to its notifyUrl. event is always TRADE_OUT_ORDER_CALLBACK.
Notification Parameters
| Field | Type | Description |
|---|---|---|
event | string | Always TRADE_OUT_ORDER_CALLBACK |
tradeId | string | Platform trade ID |
outTradeNo | string | Merchant order number |
tradeStatus | string | SUCCESS or FAIL |
tradeAmount | number | Order amount |
merchantAmount | number | Merchant settlement amount |
feeAmount | number | Merchant fee |
debitAmount | number | Total debited amount |
currency | string | Currency code |
userId | string | Merchant customer identifier |
payType | string | Payout method |
customerName | string | Customer name |
customerPhone | string | Customer phone number |
customerEmail | string | Customer email |
customerIp | string | Customer IP address |
notifyUrl | string | Notification URL |
utr | string | Transaction reference after success |
createTime | string | Creation time |
tradeCompleteTime | string | Completion time |
errMsg | string | Failure reason, returned only on failure |
Rules
- Failed notifications are retried with increasing delays.
- Respond with plain text
SUCCESS(case-insensitive). - Any other response or timeout (5 seconds) triggers a retry.
Example
{
"event": "TRADE_OUT_ORDER_CALLBACK",
"tradeId": "T20240101120000002",
"outTradeNo": "PAYOUT20240101001",
"tradeStatus": "SUCCESS",
"tradeAmount": 500.00,
"merchantAmount": 485.00,
"feeAmount": 15.00,
"debitAmount": 515.00,
"currency": "INR",
"utr": "UTR202401010001"
}Merchant response: SUCCESS
Code Examples
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;
public class OpenApiClient {
/** AES/CBC/PKCS5Padding encrypt */
public static String aesEncrypt(String plainText, String aesKeyBase64) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(aesKeyBase64);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOf(keyBytes, 16));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
return Base64.getEncoder().encodeToString(
cipher.doFinal(plainText.getBytes("UTF-8")));
}
/** AES/CBC/PKCS5Padding decrypt */
public static String aesDecrypt(String cipherBase64, String aesKeyBase64) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(aesKeyBase64);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOf(keyBytes, 16));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
return new String(
cipher.doFinal(Base64.getDecoder().decode(cipherBase64)), "UTF-8");
}
/** SHA256withRSA sign */
public static String rsaSign(String data, String privateKeyPem) throws Exception {
String key = privateKeyPem
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s+", "");
byte[] keyBytes = Base64.getDecoder().decode(key);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
PrivateKey privKey = KeyFactory.getInstance("RSA").generatePrivate(spec);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privKey);
signature.update(data.getBytes("UTF-8"));
return Base64.getEncoder().encodeToString(signature.sign());
}
/** Build and send API request */
public static void example() throws Exception {
String merchantNo = "M20240101001";
String aesKey = "your-aes-key-base64";
String merchantPK = "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----";
// 1. Business parameters
String bizJson = "{\"outTradeNo\":\"ORD001\",\"amount\":500,\"currency\":\"EUR\"}";
// 2. AES encrypt
String encryptedBody = aesEncrypt(bizJson, aesKey);
// 3. Build sign string
String requestId = "req_" + System.currentTimeMillis();
String timestamp = String.valueOf(System.currentTimeMillis());
String signData = String.join("|",
merchantNo, "1.0", requestId, timestamp, encryptedBody);
// 4. RSA sign
String sign = rsaSign(signData, merchantPK);
// 5. Send HTTP POST with headers; Body is {"payload": "<encrypted string>"}
String requestBody = "{\"payload\":\"" + encryptedBody + "\"}";
// Send requestBody as the HTTP POST body
}
}
<?php
function aesEncrypt(string $plainText, string $aesKeyBase64): string {
$key = base64_decode($aesKeyBase64);
$iv = substr($key, 0, 16);
$encrypted = openssl_encrypt($plainText, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
return base64_encode($encrypted);
}
function aesDecrypt(string $cipherBase64, string $aesKeyBase64): string {
$key = base64_decode($aesKeyBase64);
$iv = substr($key, 0, 16);
return openssl_decrypt(base64_decode($cipherBase64), 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
}
function rsaSign(string $data, string $privateKeyPem): string {
$privKey = openssl_pkey_get_private($privateKeyPem);
openssl_sign($data, $signature, $privKey, OPENSSL_ALGO_SHA256);
return base64_encode($signature);
}
// --- Example: Create Payment Order ---
$merchantNo = 'M20240101001';
$aesKey = 'your-aes-key-base64';
$privateKey = file_get_contents('/path/to/merchant_private_key.pem');
$bizJson = json_encode([
'outTradeNo' => 'ORD001',
'amount' => 500,
'currency' => 'EUR',
'payType' => 'CREDIT_CARD',
]);
$encryptedBody = aesEncrypt($bizJson, $aesKey);
$requestId = 'req_' . time();
$timestamp = (string)(time() * 1000);
$signData = implode('|', [$merchantNo, '1.0', $requestId, $timestamp, $encryptedBody]);
$sign = rsaSign($signData, $privateKey);
$ch = curl_init('https://gateway-host/api/pay/in');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
"merchantNo: $merchantNo",
'version: 1.0',
"requestId: $requestId",
"timestamp: $timestamp",
"sign: $sign",
],
CURLOPT_POSTFIELDS => json_encode(['payload' => $encryptedBody]),
]);
$response = curl_exec($ch);
curl_close($ch);