Payment links
Payment links let you create a hosted payment page and share its URL with a customer. Use a link for invoices, one-off collections, or payments where you do not want to build checkout UI yourself.
- 01Create linkCreate it on your server and store its ID with your order.
- 02Share checkoutSend only the hosted URL to the intended customer.
- 03Customer paysA redirect improves UX but does not prove payment.
- 04Verify paymentProcess the signed event and reconcile amount and order once.
Use a dashboard-issued sandbox API key on your server. Do not enter credentials into this documentation site.
Payment-link flow
| Step | Your application does | Do not rely on |
|---|---|---|
| Create | Creates a link on the server and stores its ID against your order. | A client-side API key. |
| Share | Sends the returned hosted URL to the intended customer. | Altering the amount/title after sharing without updating your order. |
| Return | Optionally receives the customer at a redirect URL. | A redirect as proof of payment. |
| Confirm | Verifies the signed payment event and link/payment state. | An unverified browser callback. |
Create a hosted link
Create the link on your server with the amount, currency, title, and optional redirect URL.
- cURL
- Node.js
- Python
- Go
curl -X POST https://sandbox.payscribe.ng/api/v1/links \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount":15000,
"currency":"NGN",
"title":"Payment for invoice INV-10021",
"description":"Website services",
"redirect":"https://example.com/payment-return"
}'
const response = await fetch('https://sandbox.payscribe.ng/api/v1/links', {
method: 'POST',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
"amount": 15000,
"currency": "NGN",
"title": "Payment for invoice INV-10021",
"description": "Website services",
"redirect_url": "https://example.com/payment-return"
}),
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
import json
payload = json.loads(r'''{
"amount": 15000,
"currency": "NGN",
"title": "Payment for invoice INV-10021",
"description": "Website services",
"redirect_url": "https://example.com/payment-return"
}''')
response = requests.post(
'https://sandbox.payscribe.ng/api/v1/links',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}", 'Content-Type': 'application/json'},
json=payload,
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"amount": 15000,
"currency": "NGN",
"title": "Payment for invoice INV-10021",
"description": "Website services",
"redirect_url": "https://example.com/payment-return"
}`)
request, err := http.NewRequest(http.MethodPost, "https://sandbox.payscribe.ng/api/v1/links", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Store the returned link ID and associate it with your internal order before sharing the returned URL.
Present and confirm payment safely
You may redirect the customer to the hosted URL or share it over an appropriate channel. A redirect_url improves customer experience after checkout, but it is not an authoritative payment confirmation.
Mark an order paid only after the relevant signed webhook has been verified and its event ID processed once. Then reconcile the amount, currency, link/order association, and final state before fulfilment.
Manage link lifecycle
Use the API to retrieve, list, update, or delete links. If a link is changed or removed, update the related order state in your system and avoid accepting an unexpected or stale payment.
Common implementation mistakes
| Mistake | Better approach |
|---|---|
| Marking an order paid on browser redirect | Confirm through a verified webhook and API record. |
| Creating links from frontend code | Create them from your backend with the API key. |
| Not storing the link ID | Persist it with your internal order/reference. |
| Processing the same webhook twice | Store event IDs with a unique constraint. |
See Webhooks for secure event handling and the Payment Links API reference for the full request and response contracts.
Was this page helpful?