Payment Endpoint¶
The payment endpoint is a server-to-server webhook URL configured on your SecPaid account. When any of your payment links is paid, SecPaid sends a POST request to this URL with the payment details.
How It Differs from callback_url¶
| Aspect | payment_endpoint | callback_url |
|---|---|---|
| Type | Server-to-server HTTP POST | Browser redirect (HTTP 302) |
| Reliability | High — independent of customer's browser | Low — customer can close tab |
| Configuration | Once, account-wide | Per link (or account default) |
| Payload | JSON body with payment details | Query parameters on URL |
| Security | Server-to-server, not user-visible | Easily spoofable by end users |
| Triggers on cancel | No | Yes (with status=cancel) |
| Use for | Order fulfillment, backend logic | Customer-facing UI feedback |
Configuration¶
Setting Your Payment Endpoint¶
- Log in to your SecPaid account
- Navigate to Settings → API & Integrations
- Enter your webhook URL in the Payment Endpoint field
- Click Save Settings
This is your account-level default — all payment links you create will use this endpoint unless overridden per-link via the API.
Requirements¶
Your endpoint URL must:
- Be publicly accessible from SecPaid's servers
- Accept POST requests with JSON body
- Return an HTTP 2xx response
- Use HTTPS in production
Request Details¶
HTTP Request¶
POST /api/secpaid/webhook HTTP/1.1
Host: yourserver.com
Content-Type: application/json
Accept: application/json
{
"ResponseCode": 1,
"data": {
"pay_id": 12345,
"note": "Invoice #1234",
"amount": 49.99,
"user_id": "usr-abc-def-123",
"status": "Success"
}
}
Payload Fields¶
| Field | Type | Always Present | Description |
|---|---|---|---|
ResponseCode |
integer | Yes | Always 1 |
data.pay_id |
integer | Yes | The linktopay_id identifying the payment |
data.note |
string | Yes | The recipient_note set during link creation (may be empty string) |
data.amount |
number | Yes | Amount paid by the customer |
data.user_id |
string | Yes | Your internal user ID (the merchant who created the link) |
data.status |
string | Yes | Always "Success" (endpoint only fires on successful payment) |
Split Link Webhooks¶
For split links, the payment endpoint is called for each recipient in the split. Each recipient's configured payment_endpoint receives the webhook with:
pay_id— same across all recipients (the link's ID)amount— the recipient's share, not the total amountuser_id— the recipient's user IDnote— therecipient_notefrom the link
Trigger Conditions¶
| Event | payment_endpoint Called? | callback_url Called? |
|---|---|---|
| Customer pays via card or bank transfer | ✅ Yes | ✅ Yes (redirect) |
| Admin confirms bank transfer | ✅ Yes | ❌ No (no browser session) |
| Customer cancels link | ❌ No | ✅ Yes (status=cancel) |
| Link deleted via API | ❌ No | ❌ No |
| Refund processed | ❌ No | ❌ No |
Implementation Examples¶
Route::post('/api/secpaid/webhook', function (Request $request) {
$payload = $request->all();
$payId = $payload['data']['pay_id'];
$amount = $payload['data']['amount'];
$status = $payload['data']['status'];
// Process the payment
$order = Order::where('payment_link_id', $payId)->first();
if ($order && $status === 'Success') {
$order->update(['status' => 'paid', 'paid_amount' => $amount]);
// Send confirmation email, fulfill order, etc.
}
return response()->json(['received' => true], 200);
});
@app.route('/api/secpaid/webhook', methods=['POST'])
def secpaid_webhook():
payload = request.get_json()
pay_id = payload['data']['pay_id']
amount = payload['data']['amount']
status = payload['data']['status']
if status == 'Success':
fulfill_order(pay_id, amount)
return jsonify({'received': True}), 200
Testing Webhooks¶
Development Environment¶
Use the development environment (app.dev.secpaid.com) with a tool like ngrok or webhook.site to receive webhooks locally:
- Start ngrok:
ngrok http 8000 - Set your
paymentEndpointto the ngrok URL - Create a test link and pay with a test card
- Observe the webhook hitting your local server
Re-sending Webhooks¶
If you missed a webhook, you can manually re-trigger it:
curl -X POST https://app.secpaid.com/api/v2/sendWebhookViaPayId \
-H "Content-Type: application/json" \
-H "token: YOUR_TOKEN" \
-d '{"pay_ids": "12345"}'
Best Practices¶
- Respond quickly — Return 200 immediately, process asynchronously if needed
- Idempotency — Handle duplicate webhooks gracefully (same
pay_idmay arrive multiple times) - Verify the payment — Check that
pay_idmatches an order you created - Use HTTPS — Always use TLS in production
- Log everything — Store raw webhook payloads for debugging
- Don't rely on order — The webhook may arrive before or after the
callback_urlredirect