Transfer Funds to Sellers
In this guide we will show how marketplaces and other exchanges can use Payment Transfer Intents to send funds to seller accounts from an existing payment.
The intent pattern gives you a pre-generated transfer ID before any funds move, and lets you confirm or cancel the transfer as a separate step. See Payment Transfer Intents for a full explanation of states, concurrency rules, and webhook events.
Getting Started
To get started, you will need a Credova Account.
Get your Secret Key
Next you will need your Secret Key. Go to your Developers section and click Reveal for your Secret Key and copy the value.
Prerequisites
This guide assumes you have already completed:
- Process Card Payments — you will need a captured payment ID.
- Onboard Sellers — you will need one or more seller account IDs.
Grab the payment id:
{
"id": "pmt_2YKewBonG4tgk12MheY3PiHDy",
"account_id": "acc_B518niGwGYKzig6vtrRVZGGGV",
"environment": "test",
...
}
And your seller account IDs:
{
"id": "acc_w6dogDaHuU6h1N5e5vfXLUYf",
"name": "Test Seller",
"type": "seller",
...
}
Step 1 — Create the Intent
Call Create Payment Transfer Intent to initialize the transfer. The response is returned in pending status and includes a pre-generated payment_transfer_id you can store in your system before committing.
curl 'https://api.publicsquare.com/payment-transfer-intents' \
-X 'POST' \
-H 'X-API-KEY: <SECRET_API_KEY>' \
-H 'Content-Type: application/json' \
-d '{
"payment_id": "pmt_2YKewBonG4tgk12MheY3PiHDy",
"currency": "USD",
"external_id": "e797ef3c-b586-4333-af90-7168d8427d85",
"items": [
{
"seller_account_id": "acc_w6dogDaHuU6h1N5e5vfXLUYf",
"external_id": "marketplace-line-1",
"amount": 10000,
"transfer_fee": 100
}
]
}'
10000 is $100.00.You should see a response similar to:
{
"id": "trnf_int_7yFLQWACr3DYDSz1xpoEAVfdq",
"account_id": "acc_B518niGwGYKzig6vtrRVZGGGV",
"environment": "test",
"status": "pending",
"payment_id": "pmt_2YKewBonG4tgk12MheY3PiHDy",
"payment_transfer_id": "trnf_7yFLQWACr3DYDSz1xpoEAVfdq",
"currency": "USD",
"total_amount": 10000,
"external_id": "e797ef3c-b586-4333-af90-7168d8427d85",
"items": [
{
"id": "trfi_int_AjkCFKAYiTsjghXWMzoXFPMxj",
"seller_account_id": "acc_w6dogDaHuU6h1N5e5vfXLUYf",
"payment_transfer_item_id": "trfi_AjkCFKAYiTsjghXWMzoXFPMxj",
"amount": 10000,
"transfer_fee": 100,
"external_id": "marketplace-line-1"
}
],
"created_at": "2024-06-30T01:02:29.212Z",
"modified_at": "2024-06-30T01:02:29.212Z"
}
Store the id (trnf_int_*) and optionally the payment_transfer_id (trnf_*) in your system before proceeding.
Step 2 — Confirm the Intent
Call Confirm Payment Transfer Intent to submit the transfer. No request body is required.
curl 'https://api.publicsquare.com/payment-transfer-intents/trnf_int_7yFLQWACr3DYDSz1xpoEAVfdq/confirm' \
-X 'POST' \
-H 'X-API-KEY: <SECRET_API_KEY>'
A successful response returns the intent in succeeded status in most cases:
{
"id": "trnf_int_7yFLQWACr3DYDSz1xpoEAVfdq",
"account_id": "acc_B518niGwGYKzig6vtrRVZGGGV",
"environment": "test",
"status": "succeeded",
"payment_id": "pmt_2YKewBonG4tgk12MheY3PiHDy",
"payment_transfer_id": "trnf_7yFLQWACr3DYDSz1xpoEAVfdq",
"currency": "USD",
"total_amount": 10000,
"created_at": "2024-06-30T01:02:29.212Z",
"modified_at": "2024-06-30T01:02:29.212Z"
}
If the transfer is not yet confirmed, the intent returns in processing status instead. Final status will arrive via the transfer_intent:update webhook.
Step 3 — Listen for Completion
Credova sends a transfer_intent:update webhook on every status transition. The entity.status field in the webhook payload reflects the final state — use it to confirm completion regardless of what the confirm response returned.
{
"id": "evnt_5jxWRFNLCAWeegrkCAG3a9DGc",
"account_id": "acc_B518niGwGYKzig6vtrRVZGGGV",
"environment": "production",
"event_type": "transfer_intent:update",
"entity_type": "PaymentTransferIntent",
"entity_id": "trnf_int_7yFLQWACr3DYDSz1xpoEAVfdq",
"entity": {
"id": "trnf_int_7yFLQWACr3DYDSz1xpoEAVfdq",
"status": "succeeded",
"payment_transfer_id": "trnf_7yFLQWACr3DYDSz1xpoEAVfdq",
"total_amount": 10000,
...
},
"created_at": "2024-06-30T01:02:29.212Z"
}
A minimal handler that marks the intent as complete in your system:
app.post('/webhooks/publicsquare', (req, res) => {
const event = req.body;
if (event.event_type === 'transfer_intent:update') {
const intent = event.entity;
if (intent.status === 'succeeded') {
// mark transfer complete in your system using intent.payment_transfer_id
} else if (intent.status === 'error') {
// intent expired or the transfer failed
}
}
res.sendStatus(200);
});
See Webhooks for delivery guarantees, signing, and retry behavior.
Polling (alternative to webhooks)
If you have not set up webhooks, you can poll Get Payment Transfer Intent until status is terminal (succeeded, cancelled, or error).
curl 'https://api.publicsquare.com/payment-transfer-intents/trnf_int_7yFLQWACr3DYDSz1xpoEAVfdq' \
-H 'X-API-KEY: <SECRET_API_KEY>'
processing, it may take seconds to minutes to reach a terminal status.Cancelling an Intent
An intent can only be cancelled while in pending status. Call Cancel Payment Transfer Intent:
curl 'https://api.publicsquare.com/payment-transfer-intents/trnf_int_7yFLQWACr3DYDSz1xpoEAVfdq/cancel' \
-X 'POST' \
-H 'X-API-KEY: <SECRET_API_KEY>'
A successful cancel returns 204 No Content.
Attempting to cancel after the intent has been confirmed returns 400 Bad Request:
{
"type": "https://httpstatuses.com/400",
"title": "Bad Request",
"status": 400,
"detail": "Payment transfer intent is not in pending status"
}
Attempting to cancel while a confirm is already in flight returns 409 Conflict:
{
"type": "https://httpstatuses.com/409",
"title": "Conflict",
"status": 409,
"detail": "Confirmation already in progress"
}
Error Reference
| HTTP Status | Endpoint | Meaning |
|---|---|---|
404 Not Found | confirm, cancel, GET /{id} | Intent ID does not exist or belongs to a different account. |
409 Conflict | confirm, cancel | The opposing operation (confirm or cancel) is already in flight. Retry after a short delay. |
400 Bad Request | cancel | Intent is not in pending status. |
202 Accepted | confirm | Transfer submitted but is not confirmed yet. The intent is processing and its terminal state will arrive via webhook. |
See Payment Transfer Intents for a deeper explanation of the concurrency model.
Conclusion
Following this guide, you have successfully transferred funds to a seller's account using Payment Transfer Intents.
Follow these guides to learn more:
- Transfer Funds from Sellers — retrieve funds from sellers via refund transfers
- Payment Transfer Intents — concept page: states, concurrency, webhook events
- Search and View Transactions
- Webhooks