Skip to main content

Payment Gateway

This API suite enables the creation of payment requests and provides payment status updates via webhooks.

Payment Overview

Quick Reference​

EndpointMethodDescription
/api/v1/paymentsPOSTCreate a new payment request
/api/v1/paymentsGETList all payment requests
/api/v1/payments/:idGETGet specific payment request

Payment Methods​

There are two primary methods to generate a payment link:

1. Direct QR Code Payment​

Use the qr_code field from your bank account to receive payments via VietQR.

  1. Call Get Bank Accounts API
  2. Use the qr_code URL from the response
  3. Payment notifications are sent to your registered webhook

2. Payment Request Creation​

Create a dedicated payment request for each transaction with custom amount, items, and redirect URLs.


API Endpoints​

Create Payment Request​

Create a new payment request for a transaction.

POST /api/v1/payments
curl -X POST 'https://api.finan.one/open/api/v1/payments' \
-H 'Content-Type: application/json' \
-H 'x-client-id: YOUR_CLIENT_ID' \
-H 'x-signature: YOUR_SIGNATURE' \
-H 'x-timestamp: 1699999999' \
-d '{
"merchant": {
"name": "Finan pte",
"phone": "02833223243",
"address": "195/10A, Dien Bien Phu, P15, Binh Thanh",
"email": "merchant@finan.one"
},
"items": [
{
"name": "So Ban Hang Pro 3 Nam",
"price": 3000000,
"quantity": 2,
"amount": 6000000
}
],
"customer": {
"name": "Nguyen Van A",
"phone": "0933450210",
"address": "15, Le Duan, q3, Quan 1",
"email": "nguyena@gmail.com"
},
"payment_method": "bank_transfer",
"account_id": "54957437-0cb5-4992-ad0e-76d26ba4ddc3",
"amount": 6000000,
"description": "thanh toan don hang",
"reference_id": "54fgi4537-0cb5-4992-ad0e-76d26ba4ddc3",
"reference_type": "invoice",
"success_redirect_url": "https://your-site.com/success",
"failure_redirect_url": "https://your-site.com/failure"
}'

Request Body​

FieldTypeRequiredDescription
merchantobjectMerchant details
merchant.namestringBusiness name
merchant.phonestringContact phone
merchant.addressstringBusiness address
merchant.emailstringContact email
itemsarrayList of items in the transaction
items[].namestringβœ…Item name
items[].priceintegerβœ…Price per unit (smallest currency unit)
items[].quantityintegerβœ…Quantity
items[].amountintegerβœ…Total amount (price * quantity)
customerobjectCustomer details
customer.namestringβœ…Customer full name
customer.phonestringContact phone
customer.addressstringBilling/shipping address
customer.emailstringEmail for notifications
payment_methodstringβœ…bank_transfer, card, or ewallet_momo
account_iduuidRequired for Shinhan bank_transfer. Get from Account API
amountintegerβœ…Total amount (smallest currency unit)
descriptionstringPayment note (max 30 characters)
reference_idstringβœ…Your unique transaction ID
reference_typestringSet to invoice to link to an Invoice
success_redirect_urlstringRedirect URL on success
failure_redirect_urlstringRedirect URL on failure
Invoice Shortcut

To create a payment for an invoice, just set reference_type: "invoice" and reference_id to the invoice ID. No merchant, items, or customer info needed.

Response​

{
"message": { "content": "TαΊ‘o thΓ nh cΓ΄ng" },
"code": 102001,
"request_id": "d0a463bc089ea27859e2ebb5d33857c3",
"data": {
"payment_request_id": "3fde043e-c3d6-40e8-9031-6eadfa02d2a2",
"payment_request_code": "PRJXH9KU",
"payment_url": "",
"bank_transfer_detail": {
"qr_code": "00020101021238570010A000000727...",
"bank_name": "MB",
"account_name": "TRAN TU THIEN",
"account_number": "0933450210",
"remark": "TTVD7ZZS"
}
}
}

Response Fields​

FieldTypeDescription
payment_request_idstringUnique payment request identifier
payment_request_codestringShort payment code
payment_urlstringPayment page URL (for card or ewallet_momo, empty for bank_transfer)
bank_transfer_detailobjectBank transfer info (for bank_transfer)
bank_transfer_detail.qr_codestringVietQR code string
bank_transfer_detail.bank_namestringBeneficiary bank code
bank_transfer_detail.account_namestringBeneficiary account name
bank_transfer_detail.account_numberstringBeneficiary account number
bank_transfer_detail.remarkstringTransfer remark (used as payment reference)
Payment Link Expiry

Each payment link is valid for 30 minutes. Create a new payment request if the previous one expires.


Get Payment Requests​

Retrieve all payment requests or filter by reference.

GET /api/v1/payments
# List all payments
curl -X GET 'https://api.finan.one/open/api/v1/payments' \
-H 'Content-Type: application/json' \
-H 'x-client-id: YOUR_CLIENT_ID' \
-H 'x-signature: YOUR_SIGNATURE' \
-H 'x-timestamp: 1699999999'

# Filter by reference
curl -X GET 'https://api.finan.one/open/api/v1/payments?reference_type=invoice&reference_id=INV-001' \
-H 'Content-Type: application/json' \
-H 'x-client-id: YOUR_CLIENT_ID' \
-H 'x-signature: YOUR_SIGNATURE' \
-H 'x-timestamp: 1699999999'

Query Parameters​

ParameterTypeDescription
reference_typestringFilter by reference type (e.g., invoice)
reference_idstringFilter by reference ID

Response​

{
"message": { "content": "Thα»±c thi API thΓ nh cΓ΄ng" },
"code": 102000,
"request_id": "abc123...",
"data": [
{
"payment_request_id": "PR-550e8400-e29b-41d4-a716-446655440000",
"paid_amount": 6000000,
"status": "paid",
"payment_method": "bank_transfer",
"amount": 6000000,
"description": "thanh toan don hang",
"reference_type": "invoice",
"reference_id": "INV-001",
"payments": [
{
"id": "PAY-001",
"amount": 6000000,
"method": "bank_transfer",
"created_at": "2024-01-20T14:30:00Z"
}
],
"merchant": {
"name": "Finan pte",
"email": "merchant@finan.one"
},
"customer": {
"name": "Nguyen Van A",
"email": "nguyena@gmail.com"
}
}
]
}

Payment Status​

StatusDescription
unpaidNo payment received (paid_amount = 0)
partial_paidPartial payment (0 < paid_amount < amount)
paidFully paid (paid_amount = amount)
extra_paidOverpaid (paid_amount > amount)
Get Single Payment

To get a specific payment request:

GET /api/v1/payments/:payment_request_id

Payment Webhook​

After successful payment collection, Finan sends a notification to your registered webhook URL.

Webhook Flow

Webhook Payload​

Your webhook will receive a POST request with the following data:

{
"id": "PAY-001",
"reference_id": "DH970422",
"payment_method": "bank_transfer",
"payment_request_id": "PR-550e8400-e29b-41d4-a716-446655440000",
"amount": 6000000,
"created_at": "2024-01-20T14:30:00Z"
}

Webhook Fields​

FieldTypeDescription
idstringUnique payment identifier
amountintegerPayment amount (smallest currency unit)
created_atdatetimePayment timestamp (ISO 8601)
payment_methodstringbank_transfer, card, or ewallet_momo
payment_request_idstringAssociated payment request ID
reference_typestringReference type (e.g., invoice)
reference_idstringYour transaction reference ID

Webhook Response​

Your webhook endpoint must respond with HTTP 200 to acknowledge receipt:

{
"status": "success",
"message": "Payment processed successfully",
"timestamp": "2024-01-20T14:30:05Z"
}
Important

Always respond with HTTP 200 even if your internal processing fails. This prevents unnecessary retries. Handle failures asynchronously in your system.

Automatic Retry​

Failed webhook deliveries are automatically retried with exponential backoff:

RetryIntervalTotal Time
1st5 min5 min
2nd10 min15 min
3rd15 min30 min
info
  • Auto-retry is enabled by default for all accounts
  • Configure retry settings in Webhook Settings
  • Events with URL Not Set or Timeout status are not auto-retried
  • Use manual retry for those cases

Verifying Webhook Signatures​

Verify webhook authenticity using the signature headers:

func verifyWebhookSignature(secretKey, method, path, body, timestamp, receivedSignature string) bool {
expectedSignature := generateSignature(secretKey, method, path, body, timestamp)
return expectedSignature == receivedSignature
}

func webhookHandler(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)

clientID := r.Header.Get("x-client-id")
signature := r.Header.Get("x-signature")
timestamp := r.Header.Get("x-timestamp")

if !verifyWebhookSignature(secretKey, "POST", "/webhook", string(body), timestamp, signature) {
http.Error(w, "Invalid signature", http.StatusUnauthorized)
return
}

// Process the webhook payload
var payment WebhookPayload
json.Unmarshal(body, &payment)

// Always respond 200
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{
"status": "success",
})
}

Next Steps​