Payments Service API
Merchant onboarding, payment intents, refunds, settlements, disputes, webhooks, and public checkout
The Payments Service is the transaction core of 505pay. It handles the full merchant lifecycle — from KYB onboarding through live payment intents, refunds, disbursements, and webhook delivery. Every payment method (QRIS, VA, GoPay, OVO, etc.) runs through this service.
Base URLs
| Environment | Hostname |
|---|---|
| Sandbox | https://sandbox.505pay.link |
| Production | https://api.505pay.link |
Server-to-server merchant calls (intents, refunds, settings) authenticate with an API key (sk_test_… in sandbox, sk_live_… in production) created at Settings → API Keys in the dashboard. Admin / console flows authenticate with a Bearer JWT issued by the Auth Service. Public checkout endpoints (/v1/public/payments/intents/*) are unauthenticated — they are called by 505pay's hosted payment page, not your server.
Payment intent lifecycle
A payment intent is the authoritative record of a payment attempt. It moves through states as the payer interacts with the PSP.
Idempotency
Pass Idempotency-Key: <uuid> on POST /v1/payments/intents to guarantee at-most-once creation. Replaying the same key returns the original intent without double-charging.
First payment in 3 steps
# 1. Create a payment intent (QRIS Dynamic — change allowed_channels for other methods)
curl -X POST https://sandbox.505pay.link/v1/payments/intents \
-H "Authorization: Bearer $PAYOPS_SECRET_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1234-v1" \
-d '{
"merchant_id": "'"$PAYOPS_MERCHANT_ID"'",
"amount_idr": 50000,
"max_uses": 1,
"description": "Order #1234",
"allowed_channels": ["qris"]
}'
# 2. Show the payer the checkout — redirect to intent.checkout_url, or render intent.qr_string
# 3. Listen for the payment.completed webhook on your registered endpointconst intent = await fetch('https://sandbox.505pay.link/v1/payments/intents', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.PAYOPS_SECRET_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': 'order-1234-v1',
},
body: JSON.stringify({
merchant_id: process.env.PAYOPS_MERCHANT_ID,
amount_idr: 50_000,
max_uses: 1,
description: 'Order #1234',
allowed_channels: ['qris_dynamic'],
}),
}).then((r) => r.json());
console.log(intent.checkout_url); // hosted page; or render intent.qr_string yourselfimport os, requests
intent = requests.post(
'https://sandbox.505pay.link/v1/payments/intents',
headers={
'Authorization': f"Bearer {os.environ['PAYOPS_SECRET_KEY']}",
'Idempotency-Key': 'order-1234-v1',
},
json={
'merchant_id': os.environ['PAYOPS_MERCHANT_ID'],
'amount_idr': 50_000,
'max_uses': 1,
'description': 'Order #1234',
'allowed_channels': ['qris_dynamic'],
},
).json()
print(intent['checkout_url'])import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"github.com/google/uuid"
)
body, _ := json.Marshal(map[string]any{
"merchant_id": os.Getenv("PAYOPS_MERCHANT_ID"),
"amount_idr": 50_000,
"max_uses": 1,
"description": "Order #1234",
"allowed_channels": []string{"qris"},
"metadata": map[string]string{"order_id": "1234"},
})
req, _ := http.NewRequest("POST",
"https://sandbox.505pay.link/v1/payments/intents",
bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("PAYOPS_SECRET_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", "order-1234-"+uuid.New().String())
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
// intent.checkout_url is in the JSON response
var intent map[string]any
_ = json.NewDecoder(resp.Body).Decode(&intent)
fmt.Println(intent["checkout_url"])<?php
require 'vendor/autoload.php'; // Guzzle
$client = new \GuzzleHttp\Client();
$res = $client->post('https://sandbox.505pay.link/v1/payments/intents', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('PAYOPS_SECRET_KEY'),
'Content-Type' => 'application/json',
'Idempotency-Key' => 'order-1234-' . \Ramsey\Uuid\Uuid::uuid4(),
],
'json' => [
'merchant_id' => getenv('PAYOPS_MERCHANT_ID'),
'amount_idr' => 50000,
'max_uses' => 1,
'description' => 'Order #1234',
'allowed_channels' => ['qris_dynamic'],
'metadata' => ['order_id' => '1234'],
],
]);
$intent = json_decode($res->getBody(), true);
echo $intent['checkout_url']; // hosted page; or render $intent['qr_string'] yourselfSee the full Accept your first payment recipe for the webhook + fulfilment loop.
Endpoint reference
Merchant onboarding (KYB)
Create and submit merchants through KYB. Progress through draft → submitted → approved/rejected.
Get current agreement template
List KYB document requirements
List non-prohibited industries
Get a merchant
List beneficial owners
List documents
List caller's merchants
Create a merchant draft
Record agreement acceptance
Add a beneficial owner
Upload a KYB document
Submit merchant through KYB
Partial-update a draft merchant
Update beneficial owner
Delete a beneficial owner
Team
Add and remove merchant team members.
Payment intents
Create, list, and cancel payment intents. Track attempt history.
Refunds
Issue full or partial refunds and list them.
Settlements
View settlement batches and payout settings.
Disbursements
Create, approve/reject disbursements with two-eyes workflow. Manage recipients and batch uploads.
List disbursements
Get a single disbursement
List uploaded disbursement batches
Get a disbursement batch (with row-level status)
List disbursement recipients (address book)
Get a disbursement recipient
List disbursement batches (paginated)
Get a disbursement batch
Get line items for a disbursement batch
Create a new disbursement (status=pending under threshold, pending_approval over)
Approve a pending_approval disbursement (two-eyes; AC-42.5)
Reject a pending_approval disbursement (two-eyes; AC-42.5)
Upload a CSV batch of disbursements (AC-42.6 — idempotent via csv_sha256)
Create a disbursement recipient
Verify a recipient's bank account via PSP account validation
PSP disbursement webhook receiver — Flip and other providers (AC-42.8)
UOB advisory disbursement webhook (JWKS-kid signed notification)
Update a disbursement recipient
Deactivate a disbursement recipient
Payouts
Merchant payout accounts, schedules, and payout history.
List a merchant's payout accounts
Get a merchant's payout schedule
List the merchant's historical payouts (paginated)
Get a single payout (with line items)
Get the projected next payout for the merchant
Reject a pending payout account (admin)
Revoke a verified payout account (admin; opens a new pending row)
Mark a pending payout account as verified (admin)
Pause a merchant's payout schedule (admin)
Resume a merchant's payout schedule (admin)
Submit a payout bank account (lands as pending_verification)
Update a merchant's payout schedule
Pricing & billing
Tenant-level pricing templates, per-merchant overrides, billing charges, and statements.
Reporting & transactions
Revenue KPIs, transaction ledger, drill-through detail, and async CSV export.
Merchant payments dashboard snapshot
List transactions (paginated)
Drill-through detail for one payment intent (AC-6.3, AC-6.5)
Export transactions as CSV or XLSX
List async export jobs for the caller's tenant (AC-6.12)
Get an export job by id (AC-6.12)
Enqueue an async export job (AC-6.12)
Disputes (chargebacks)
View, respond to, and accept chargeback disputes.
Payment channels & routing
Enable payment channels and manage routing rules.
PSP credentials
Manage Payment Service Provider credentials and rotation.
API keys
Manage API keys — create, update, revoke, and monitor for leaked keys.
Webhooks
Webhook subscriptions for event delivery.
Public checkout (unauthenticated)
Called by 505pay's hosted payment page — no Authorization header required.
PSP webhooks (inbound)
Inbound webhook endpoint called by PSP providers.
Health & metadata
Liveness and readiness probes.
Ungrouped operations
84 operations in the spec are not listed in the groups file. Add their operationIds to content/docs/api/payments.groups.json.
List owner downstream fee schedules
List owner per-period downstream-fee invoices
Get an owner invoice with per-event attribution lines
Get the owner payout reserve percentage
List all tenants' Straventa subscriptions (platform admin)
List Straventa plans
List PSP settlement reconciliation statements
Get a settlement statement with its reconciled lines
List subscriptions for a merchant
Get a single subscription
List unified payments cases (aggregates disputes + refunds)
Get a single case by composite id ({type}:{uuid})
Per-channel revenue breakdown for a date range
Conversion funnel (Created / Viewed / Charge submitted / Paid)
Metric + grain timeseries
Top payment links by paid amount for a date range
Export an approved manual_file batch as a bank worksheet (CSV or XLSX)
List merchant invoices (paginated)
Get a single merchant invoice
Download the rendered invoice PDF
List customers for a merchant
Get customer profile with aggregates
GDPR Article 15 export — full customer record as JSON
Get the active GDPR delete job for a customer (404 when none)
List payment intents for a customer
List notes for a customer (most-recent first)
List refunds tied to a customer (most-recent first)
Get merchant KYC/KYB verification state
List the caller's KYC documents (merchant-facing)
Fetch metadata for a single KYC document
List the platform KYC reviewer queue
List the platform KYC/KYB verification review queue
Get public refund status (HMAC-authenticated, no JWT required)
Stream the merchant's disbursement batches as a CSV file
Stream the merchant's settlements as a CSV file
Get merchant customer-email settings (reply_to + branding + verification state)
Read merchant notification preferences
List webhook delivery attempts for a subscription
List dead-letter queue rows for a subscription
List the canonical event catalogue
Get the webhook dispatcher's retry policy
Get the tenant's active Straventa subscription
Download invoice PDF (public)
View subscription portal (anonymous)
Anonymous HMAC-token verification of merchant reply_to (mailbox round-trip)
Create an owner downstream fee schedule
Create or update a Straventa plan (platform admin)
Ingest and reconcile a PSP settlement statement
Create a recurring subscription
Merchant-initiated cancel
Upload the bank/portal result file to settle or fail disbursement rows
Preview fee + total debit before submitting a disbursement (AC-42.9)
Sandbox 3-D-Secure callback for the card_3ds channel
Re-send the invoice email
Void an invoice
GDPR Article 17 — schedule customer erasure
Append a free-form note to a customer profile
Bulk-import customers from a CSV file
Upload a KYC document (merchant-facing, multipart)
Re-upload (replace) a KYC document (multipart)
Approve a KYC document (terminal transition)
Reject a KYC document (terminal transition)
Transition document from uploaded to under_review
Dispose a needs_review verification (verified or rejected)
File a recon question / settlement-variance dispute
Rotate an API key
Send mailbox-round-trip verification email to the current reply_to
Replay a single webhook delivery
Replay every dead-letter row for a subscription
Rotate a webhook subscription's signing secret
Subscribe the tenant to a Straventa plan
Cancel the tenant's Straventa subscription
Record a VA Copy-button click (anonymous telemetry)
Subscriber-initiated cancel
Pause (active → paused)
Resume (paused → active)
Update an owner downstream fee schedule
Set the owner payout reserve percentage
Update merchant customer-email settings
Update merchant notification preferences
Transition a case status (delegates to underlying dispute/refund)
Update a customer note (replaces body; bumps updated_at)
Update subscriber email (rotates portal token)
Hard-delete a customer note (irreversible)
Was this page helpful?