Payment Links
Create shareable payment links · Email & SMS delivery · Card & Google Pay acceptance
Summary
Payment Links allow merchants to request payment from customers without building a custom checkout. The merchant creates a link via the API, shares it by email or SMS, and the customer pays through a hosted payment page.
Payment Links support manual card entry, saved cards, and Google Pay. Optional 3D Secure can be enabled per link.
Payment Link Lifecycle
- Create — Merchant creates a payment link via the API with customer, amount, and optional description.
- Share — RapidCents automatically sends the link to the customer via email. The merchant can also resend by email or SMS.
- View — Customer opens the link and sees the hosted payment page with amount, merchant details, and payment form.
- Pay — Customer enters card details (or uses Google Pay) and submits payment. 3D Secure step-up is triggered if enabled.
- Result — On approval, the link status changes to
PAID. Both merchant and customer receive a payment confirmation notification.
Payment Link Statuses
CREATED
New link, unpaid. Can be edited, resent, or deleted.
PAID
Successfully paid. Cannot be edited or deleted.
EXPIRED
No longer payable. Expired due to max attempts, 3DS failure, or due date.
Expiry Reasons
| Code | Reason | Description |
|---|---|---|
1 | Max Attempts | Customer exceeded the maximum number of failed payment attempts |
2 | 3D Secure Failure | 3D Secure authentication failed |
3 | Phone Verification Failure | Phone verification step failed |
4 | Due Date Expired | Linked invoice due date has passed |
Processing Flow
Authentication
| Scope | Auth Required | Details |
|---|---|---|
| Merchant API (create, list, update, delete, resend) | Yes | Bearer <access_token> + business ID in URL |
| Customer (Public) (view, pay) | No | Payment link ID in URL acts as access token |
Create Payment Link
POST /api/{business_id}/payment_links
{
"customerId": "customer-uuid",
"amount": 50.00,
"description": "Invoice #1001 - Web Design Services",
"customInvoiceNumber": "INV-1001",
"merchant_description": "Internal note for this link",
"surcharge": 2.50,
"threeDSecure": true
}
Create Fields
| Field | Type | Required | Description |
|---|---|---|---|
customerId | string | Yes | Customer UUID |
amount | number | Yes | Payment amount |
description | string | No | Description shown to customer |
customInvoiceNumber | string | No | Custom invoice reference |
merchant_description | string | No | Internal note (not shown to customer) |
surcharge | number | No | Additional surcharge amount |
threeDSecure | boolean | No | Enable 3D Secure for this link |
{
"ok": true,
"payment_link": {
"id": "pl-uuid",
"amount": 50.00,
"status": "CREATED",
"url": "https://pay.rapidcents.com/payment-links/pl-uuid",
"created_at": "2026-02-26T12:00:00Z"
}
}
List Payment Links
GET /api/{business_id}/payment_links
{
"ok": true,
"payment_links": {
"data": [ /* array of payment link objects */ ],
"current_page": 1,
"last_page": 5,
"per_page": 10,
"total": 48
},
"filters": {
"fromDate": "2026-01-01",
"toDate": "2026-02-26"
}
}
Get Link Details
GET /api/{business_id}/payment_links/{payment_link_id}
Update Payment Link
PUT /api/{business_id}/payment_links/{payment_link_id}
A payment link can only be updated when its status is CREATED and any attached contract is unsigned.
{
"amount": 75.00,
"description": "Updated description"
}
Delete Payment Link
DELETE /api/{business_id}/payment_links/{payment_link_id}
A payment link can only be deleted when its status is not PAID and any attached contract is unsigned.
Resend Payment Link
POST /api/{business_id}/payment_links/{payment_link_id}/resend
Resends the payment link notification to the customer via email. If the link has already been paid, a receipt is sent instead.
Payment Attempts
GET /api/{business_id}/payment_links/{payment_link_id}/attempts
Returns a list of all payment attempts made on this link, including failed attempts with decline reasons.
View Payment Page (Public)
GET /api/payment_links/{payment_link_id}/pay
This is a public endpoint — no authentication required. The payment link ID in the URL serves as the access token.
{
"ok": true,
"paymentLink": {
"id": "pl-uuid",
"amount": 50.00,
"description": "Invoice #1001",
"status": "CREATED"
},
"business": { /* business details */ },
"customerEmail": "[email protected]",
"logo": "https://...",
"three_d_secure": true
}
Pay with Card (Public)
POST /api/payment_links/{payment_link_id}/pay
{
"cardData": {
"cardNumber": "4111111111111111",
"month": "12",
"year": "2028",
"cvv": "123",
"nameOnCard": "John Doe",
"saveCard": false,
"address": {
"line1": "123 Main St",
"postalCode": "M5V 2T6"
}
}
}
Card Payment Fields
| Field | Type | Required | Description |
|---|---|---|---|
cardData.cardNumber | string | Yes | Full card number |
cardData.month | string | Yes | Expiration month (2 digits) |
cardData.year | string | Yes | Expiration year |
cardData.cvv | string | Yes | CVV |
cardData.nameOnCard | string | Yes | Cardholder name |
cardData.saveCard | boolean | No | Save card for future payments |
cardData.address.line1 | string | Yes | Billing address |
cardData.address.postalCode | string | Yes | Billing postal code |
With 3D Secure
If the payment link has threeDSecure enabled, include the 3DS session data:
{
"cardData": { /* ... card fields ... */ },
"ddd": { /* 3D Secure authentication data */ },
"dddSessionID": "3ds-session-uuid"
}
{
"ok": true,
"result": {
"status": "Approved",
"auth_code": "123456",
"amount": 50.00
}
}
3D Secure (3DS) Flow
When a payment link has threeDSecure enabled, the customer must complete 3D Secure authentication before the payment is processed. This adds an extra layer of cardholder verification.
3D Secure is applied only when both the business has 3DS enabled and the individual payment link has threeDSecure: true.
3DS Step-by-Step Flow
- View Payment Link —
GET /api/payment_links/{id}/payreturnsthree_d_secure: trueindicating 3DS is required. - Init (Card Lookup) — Client calls
POST /api/{business_id}/ddd/initwith card details to check 3DS support and create a session. - Authenticate — If init returns
DDD_INVOKE, client callsPOST /api/{business_id}/ddd/authenticateto start authentication. - Challenge (if required) — If authenticate returns status
C, the customer is redirected to the bank's ACS page to complete the challenge. - Pay — Client submits the payment with
ddd(3DS result) anddddSessionIDtoPOST /api/payment_links/{id}/pay.
Step 1: Init (Card Lookup)
POST /api/{business_id}/ddd/init
{
"cardData": {
"cardNumber": "4111111111111111",
"month": "12",
"year": "2028"
},
"paymentLinkID": "payment-link-uuid"
}
Init Response — Frictionless (no challenge needed)
{
"ok": true,
"status": "DDD_FRICTIONLESS",
"data": {
"threeDSServerTransID": "trans-id",
"association": "visa",
"sessionID": "session-id"
}
}
Init Response — Challenge Required
{
"ok": true,
"status": "DDD_INVOKE",
"data": {
"threeDSMethodURL": "https://acs.bank.com/3ds-method",
"threeDSMethodData": "base64-data",
"threeDSServerTransID": "trans-id",
"association": "visa",
"sessionID": "session-id"
}
}
Init Response — 3DS Not Supported
{
"ok": false,
"status": "DDD_NOT_SUPPORTED",
"message": "Card does not support 3D Secure"
}
Step 2: Authenticate
POST /api/{business_id}/ddd/authenticate
{
"threeDSServerTransID": "from-init-response",
"sessionID": "from-init-response",
"email": "[email protected]",
"cardData": {
"nameOnCard": "John Doe",
"cardNumber": "4111111111111111",
"month": "12",
"year": "2028",
"cvv": "123"
},
"amount": 50.00
}
Authenticate Response — Success (Frictionless)
{
"status": "Y",
"message": "OK",
"data": {
"transStatus": "Y",
"authenticationValue": "base64-cavv-value",
"eci": "05"
},
"sessionID": "session-id"
}
Authenticate Response — Challenge Required
{
"status": "C",
"message": "OK",
"data": {
"creq": "base64-challenge-request",
"acsURL": "https://acs.bank.com/challenge",
"threeDSSessionData": "trans-id"
},
"sessionID": "session-id"
}
Step 3: Challenge (Bank Redirect)
When the authenticate response returns status C, redirect the customer to the acsURL with the creq data. The bank will present a challenge (OTP, biometric, etc.). After completion, the bank posts the result back to your callback URL.
Step 4: Pay with 3DS Data
After 3DS authentication completes (frictionless or challenge), include the 3DS result in the payment request:
Frictionless Payment
{
"cardData": {
"cardNumber": "4111111111111111",
"month": "12",
"year": "2028",
"cvv": "123",
"nameOnCard": "John Doe",
"address": {
"line1": "123 Main St",
"postalCode": "M5V 2T6"
}
},
"ddd": {
"transStatus": "Y",
"authenticationValue": "base64-cavv-value",
"eci": "05",
"association": "visa"
},
"dddSessionID": "session-id"
}
Challenge Payment
{
"cardData": {
"cardNumber": "4111111111111111",
"month": "12",
"year": "2028",
"cvv": "123",
"nameOnCard": "John Doe",
"address": {
"line1": "123 Main St",
"postalCode": "M5V 2T6"
}
},
"ddd": {
"creq": "base64-challenge-request"
},
"dddSessionID": "session-id"
}
3DS Authentication Statuses
| Status | Meaning | Action |
|---|---|---|
Y | Authenticated | Proceed with payment (include ddd data) |
A | Attempted | Proceed with payment (liability shift may apply) |
C | Challenge required | Redirect customer to acsURL for bank challenge |
N | Not authenticated | Authentication failed — do not proceed |
U | Unavailable | 3DS server unavailable — merchant decides risk |
R | Rejected | Authentication rejected — do not proceed |
3DS Endpoints Summary
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/{business_id}/ddd/init | Start 3DS session & card lookup |
| POST | /api/{business_id}/ddd/authenticate | Run 3DS authentication |
Pay with Google Pay (Public)
POST /api/payment_links/{payment_link_id}/pay
{
"googlePay": {
"payment_token": "{\"signature\":\"...\",\"protocolVersion\":\"ECv2\",\"signedMessage\":\"...\"}",
"amount": 50.00,
"currency": "CAD"
},
"cardData": {
"address": {
"postalCode": "M5V 2T6",
"line1": "123 Main St"
}
}
}
Google Pay Fields
| Field | Type | Required | Description |
|---|---|---|---|
googlePay.payment_token | string | Yes | Raw Google Pay ECv2 token |
googlePay.amount | number | Yes | Payment amount (min 0.01) |
googlePay.currency | string | Yes | 3-letter currency code (e.g., CAD) |
cardData.address.postalCode | string | No | Billing postal code |
cardData.address.line1 | string | No | Billing address |
All Endpoints
Merchant API (Authenticated)
| Method | Endpoint | Description |
|---|---|---|
| POST | /{business_id}/payment_links | Create payment link |
| GET | /{business_id}/payment_links | List payment links |
| GET | /{business_id}/payment_links/{id} | Get link details |
| PUT | /{business_id}/payment_links/{id} | Update link |
| DELETE | /{business_id}/payment_links/{id} | Delete link |
| GET | /{business_id}/payment_links/export | Export links |
| POST | /{business_id}/payment_links/{id}/resend | Resend link / receipt |
| GET | /{business_id}/payment_links/{id}/attempts | View payment attempts |
3D Secure API (Authenticated)
| Method | Endpoint | Description |
|---|---|---|
| POST | /{business_id}/ddd/init | Start 3DS session & card lookup |
| POST | /{business_id}/ddd/authenticate | Run 3DS authentication |
Customer API (Public)
| Method | Endpoint | Description |
|---|---|---|
| GET | /payment_links/{id}/pay | View payment page |
| POST | /payment_links/{id}/pay | Submit payment |
Query Filters (List)
| Parameter | Type | Description |
|---|---|---|
fromDate | date | Filter links created from this date |
toDate | date | Filter links created up to this date |
status | string | Filter by status: CREATED, PAID, EXPIRED |
customInvoiceNumber | string | Search by invoice number |
customer | string | Search by customer name, email, or ID |
perPage | integer | Results per page (default: 10) |
Card Data Fields Reference
| Field | Type | Required | Description |
|---|---|---|---|
cardData.cardNumber | string | Yes | Full card number (PAN) |
cardData.month | string | Yes | Expiration month (01-12) |
cardData.year | string | Yes | Expiration year |
cardData.cvv | string | Yes | Card verification value |
cardData.nameOnCard | string | Yes | Cardholder name |
cardData.saveCard | boolean | No | Save card for future use |
cardData.address.line1 | string | Yes | Billing address line 1 |
cardData.address.postalCode | string | Yes | Billing postal / ZIP code |
Error Handling
| Scenario | HTTP Status | Description |
|---|---|---|
| Validation error | 422 | Missing or invalid fields |
| Unauthorized | 401 | Missing or invalid Bearer token (merchant API only) |
| Link not found | 404 | Payment link ID does not exist |
| Already paid | 200 | Returns receipt data so the customer can view the receipt |
| Link expired | 200 | ok: false with expiry reason |
| Max attempts exceeded | 200 | Link is automatically expired after max failed attempts |
| Transaction declined | 200 | ok: false with decline reason from processor |
{
"ok": false,
"error": "Payment link not found",
"message": "Payment link not found"
}
Notifications
| Event | Channel | Recipient | Description |
|---|---|---|---|
| Link Created | Customer | Automatically sent when a payment link is created | |
| Link Resent | Customer | Sent when merchant triggers resend | |
| Payment Successful | Customer & Merchant | Confirmation with receipt when payment is approved |
SMS delivery is also supported. Merchants can share payment links via SMS through the dashboard or API.