P2PPay Company API (Peer Matching) (1.0.0)

Download OpenAPI specification:

Company Payment API for the P2PPay peer-matching flow. Peer matching PayIn transactions do not use a provider-hosted QR; instead, the end user transfers the exact amount to a configured peer bank account, and the company uploads the resulting bank slip to reconcile the transaction.

This API enables companies to:

  • Get available peer-matching payment channels for PayIn and PayOut
  • Create peer-matching PayIn and PayOut transactions
  • Upload a bank slip image so the platform can auto-match it against a pending PayIn
  • Check transaction status and trigger status callbacks
  • Check account balances
  • Get bank codes list

Typical Peer-Matching PayIn Flow

  1. Call POST /payIn/create with channel/type + peerBank/peerAccountNumber (the sender's bank & account). The response contains the destination peer bank account (bank, accountNumber, peerAccountName) the user must transfer to, plus the exact amount (the platform may adjust the requested amount by cents to guarantee uniqueness).
  2. The end user transfers the exact amount from the sender peer account.
  3. Company calls POST /slip/upload with the bank-slip image (multipart). The platform verifies the slip QR, enforces duplicate/destination/time rules, then attempts to auto-match it to the pending PayIn.
  4. On match the transaction transitions to the next state and a callback is delivered to callbackUrl. The company can also poll with POST /status/{refId}.

Signature Calculation (for PayIn/PayOut create)

  1. Sort request body fields (excluding signature) by key alphabetically
  2. Concatenate as key1=value1key2=value2... (no quoting/escaping, values must be string or null)
  3. signature = md5(payload + signKey) as lowercase hex

Channels

Peer-matching payment channel operations

Get available PayIn channels

Retrieves the list of available PayIn channels for the authenticated company

Authorizations:
ApiKeyAuth

Responses

Response samples

Content type
application/json
{
  • "status": 0,
  • "message": "Success",
  • "success": true,
  • "code": 0,
  • "channels": [
    ]
}

Get available PayOut channels

Retrieves the list of available PayOut channels for the authenticated company

Authorizations:
ApiKeyAuth

Responses

Response samples

Content type
application/json
{
  • "status": 0,
  • "message": "Success",
  • "success": true,
  • "code": 0,
  • "channels": [
    ]
}

Get PayIn channel configuration

Retrieves per-channel configuration (min/max amount, allowed exact amounts) for PayIn channels available to the authenticated company. Useful before calling POST /payIn/create to pre-validate amounts.

Authorizations:
ApiKeyAuth

Responses

Response samples

Content type
application/json
{
  • "status": 0,
  • "message": "Success",
  • "success": true,
  • "code": 0,
  • "channels": [
    ]
}

Get PayOut channel configuration

Retrieves per-channel configuration (min/max amount, allowed exact amounts) for PayOut channels available to the authenticated company.

Authorizations:
ApiKeyAuth

Responses

Response samples

Content type
application/json
{
  • "status": 0,
  • "message": "Success",
  • "success": true,
  • "code": 0,
  • "channels": [
    ]
}

PayIn

Peer-matching PayIn transaction operations

Get available PayIn channels

Retrieves the list of available PayIn channels for the authenticated company

Authorizations:
ApiKeyAuth

Responses

Response samples

Content type
application/json
{
  • "status": 0,
  • "message": "Success",
  • "success": true,
  • "code": 0,
  • "channels": [
    ]
}

Get PayIn channel configuration

Retrieves per-channel configuration (min/max amount, allowed exact amounts) for PayIn channels available to the authenticated company. Useful before calling POST /payIn/create to pre-validate amounts.

Authorizations:
ApiKeyAuth

Responses

Response samples

Content type
application/json
{
  • "status": 0,
  • "message": "Success",
  • "success": true,
  • "code": 0,
  • "channels": [
    ]
}

Create a peer-matching PayIn transaction

Creates a new peer-matching PayIn transaction (deposit/top-up).

The platform reserves a destination peer bank account for the company and returns it in the response (bank, accountNumber, peerAccountName). The end user must transfer the exact returned amount from peerBank/peerAccountNumber, then the company calls POST /slip/upload with the bank-slip image to reconcile the transaction.

Minimum Amount: 100

Peer-Matching Notes:

  • peerBank and peerAccountNumber identify the sender account the end user will transfer from. Providing them improves auto-match accuracy.
  • The platform may adjust the requested amount by small cents so each pending peer transaction has a unique (destination, amount) pair. Use the amount field in the response as the amount the user must send.

Signature Calculation:

  1. Sort all request body fields (except signature) by key alphabetically
  2. Create payload string: key1=value1key2=value2...
  3. Compute MD5 hash of payload + signKey
  4. Use hex string of the hash as signature
Authorizations:
ApiKeyAuth
Request Body schema: application/json
required
channel
required
string

Payment channel to use

type
required
string
Enum: "PAYIN" "TOPUP"

Transaction type

externalRefId
required
string

Unique external reference ID provided by company

required
string or number

Requested amount (minimum 100). Server may adjust by cents to ensure uniqueness.

callbackUrl
string or null <uri>

URL to receive status callbacks (http/https only)

memberName
string

Member name

memberPhoneNumber
string

Member phone number

peerBank
string

Sender bank code for peer matching. Recommended to improve auto-match accuracy on slip upload.

peerAccountNumber
string

Sender bank account number for peer matching

signature
string

MD5 signature for request validation

Responses

Request samples

Content type
application/json
{
  • "channel": "BANKTRANSFER",
  • "type": "PAYIN",
  • "externalRefId": "ORDER-12345",
  • "requestedAmount": "1000.00",
  • "memberName": "John Doe",
  • "memberPhoneNumber": "0812345678",
  • "peerBank": "SCB",
  • "peerAccountNumber": "1234567890",
  • "signature": "5d41402abc4b2a76b9719d911017c592"
}

Response samples

Content type
application/json
{
  • "status": 0,
  • "message": "Success",
  • "success": true,
  • "code": 0,
  • "transaction": {
    }
}

Upload a bank-slip image (peer-matching)

Uploads a bank-slip image to reconcile a pending peer-matching PayIn transaction. The upload is multipart/form-data with a single image file field.

Processing steps (server-side):

  1. Store the image and create a bank_slip_image record with status PENDING plus an associated transaction_evidence record.
  2. If slip verification is enabled, extract the QR from the slip and call the slip-verify provider to get the decoded transfer data.
  3. Reject duplicate slips (same verified slip hash) — returns DUP MATCHED and the evidence is rejected.
  4. Validate that the decoded destination account matches one of the company's configured destination accounts (skipped for peer-only companies where destinations are dynamic).
  5. Attempt to auto-match the slip to a pending BankExpect for the company using amount + peerBank + peerAccountNumber + time window. On a unique match, the corresponding PayIn transaction is confirmed and a status callback is scheduled.

Auto-match outcomes (recorded on the evidence / returned via later status):

  • MATCHED <externalRefId> — slip bound to a pending transaction
  • NOT MATCH - NO TXCANDIDATE FOUND
  • NOT MATCH - MULTIPLE TXCANDIDATE FOUND
  • NOT MATCH - OUT OF TIME (N hours ago) — slip timestamp older than 24h
  • NOT MATCH - ALREADY MATCHED <externalRefId>
  • NOT MATCH - REFUNDED

The HTTP response returns the stored image(s) metadata. The final match result is reported asynchronously via POST /status/{refId} or the status callback.

Authorizations:
ApiKeyAuth
Request Body schema: multipart/form-data
required
image
required
string <binary>

Bank slip image file (PNG / JPEG / WebP). Sent as multipart/form-data field image.

Responses

Response samples

Content type
application/json
[]

PayOut

Peer-matching PayOut transaction operations

Get available PayOut channels

Retrieves the list of available PayOut channels for the authenticated company

Authorizations:
ApiKeyAuth

Responses

Response samples

Content type
application/json
{
  • "status": 0,
  • "message": "Success",
  • "success": true,
  • "code": 0,
  • "channels": [
    ]
}

Get PayOut channel configuration

Retrieves per-channel configuration (min/max amount, allowed exact amounts) for PayOut channels available to the authenticated company.

Authorizations:
ApiKeyAuth

Responses

Response samples

Content type
application/json
{
  • "status": 0,
  • "message": "Success",
  • "success": true,
  • "code": 0,
  • "channels": [
    ]
}

Create a peer-matching PayOut transaction

Creates a new peer-matching PayOut transaction (withdrawal).

peerBank and peerAccountNumber are the destination account owned by the end user / member.

Minimum Amount: 300

Signature Calculation:

  1. Sort all request body fields (except signature) by key alphabetically
  2. Create payload string: key1=value1key2=value2...
  3. Compute MD5 hash of payload + signKey
  4. Use hex string of the hash as signature
Authorizations:
ApiKeyAuth
Request Body schema: application/json
required
channel
required
string

Payment channel to use

type
required
string
Value: "PAYOUT"

Transaction type

externalRefId
required
string

Unique external reference ID provided by company

required
string or number

Requested amount (minimum 300)

callbackUrl
string or null <uri>

URL to receive status callbacks

memberName
string

Member name

memberPhoneNumber
string

Member phone number

peerBank
string

Destination (payee) bank code

peerAccountNumber
string

Destination (payee) account number

signature
string

MD5 signature for request validation

Responses

Request samples

Content type
application/json
{
  • "channel": "BANKTRANSFER",
  • "type": "PAYOUT",
  • "externalRefId": "WITHDRAW-12345",
  • "requestedAmount": "5000.00",
  • "memberName": "Jane Doe",
  • "memberPhoneNumber": "0823456789",
  • "peerBank": "KBANK",
  • "peerAccountNumber": "9876543210",
  • "signature": "5d41402abc4b2a76b9719d911017c592"
}

Response samples

Content type
application/json
{
  • "status": 0,
  • "message": "Success",
  • "success": true,
  • "code": 0,
  • "transaction": {
    }
}

Get peer-match fulfillment details for a PayOut (by refId)

Returns the peer-match fulfillment state for a peer-matching PayOut (withdraw) transaction, identified by internal refId.

Use this when you need to see which peer deposits are being used to fulfill a withdraw order and how much is still outstanding. The regular POST /status/{refId} endpoint only returns top-level transaction state; this endpoint adds the underlying PeerWithdrawRequest accounting (amount / matchedAmount / pendingAmount / remainingAmount) plus each PeerToPeerMatch that has been attached so far.

Accounting semantics:

  • amount — total amount the platform is trying to fulfill from peers
  • matchedAmount — amount already confirmed by peer deposits
  • pendingAmount — amount locked to PENDING matches awaiting slip confirmation
  • remainingAmount = amount - matchedAmount - pendingAmount

Match statuses (peerMatches[].status):

  • PENDING — deposit reserved against this withdraw, awaiting slip confirmation
  • CONFIRMED — deposit slip verified, amount added to matchedAmount
  • REJECTED — match cancelled (e.g. slip verification failed)
  • EXPIRED — slip window passed without confirmation; amount released

Withdraw request statuses (peerWithdrawRequest.status):

  • OPEN — still accepting new matches
  • FULFILLED — fully covered (no more matches will be created)
  • STALE — expired without full fulfillment

Privacy / compactness: Only the destination account (destBank / destAccountNumber) the peer depositor paid to, and a signed, time-limited slipImageUrl pointing at the uploaded slip, are returned per match. Counterparty (depositor) identifiers, member info, the raw slip QR string, and the full slip-verify JSON are intentionally omitted — this endpoint is designed to be compact and cheap to poll; use the admin peer-monitor API for full forensics.

Authorizations:
ApiKeyAuth
path Parameters
refId
required
string

Internal transaction reference ID of the PayOut

Responses

Response samples

Content type
application/json
{
  • "status": 0,
  • "message": "Success",
  • "success": true,
  • "code": 0,
  • "refId": "TX-20260422-001",
  • "externalRefId": "WITHDRAW-12345",
  • "transactionStatus": "PROCESSING",
  • "requestedAmount": "5000.00",
  • "amount": "5000.00",
  • "peerWithdrawRequest": {
    },
  • "peerMatches": [
    ],
  • "summary": {
    }
}

Get peer-match fulfillment details for a PayOut (by externalRefId)

Same as POST /payOut/peerMatches/{refId} but looks up the PayOut by the external reference ID originally provided by the company when the transaction was created.

Authorizations:
ApiKeyAuth
path Parameters
externalRefId
required
string

External transaction reference ID (provided by company) of the PayOut

Responses

Response samples

Content type
application/json
{
  • "status": 0,
  • "message": "Success",
  • "success": true,
  • "code": 0,
  • "refId": "TX-20260422-001",
  • "externalRefId": "WITHDRAW-12345",
  • "transactionStatus": "PROCESSING",
  • "requestedAmount": "5000.00",
  • "amount": "5000.00",
  • "peerWithdrawRequest": {
    },
  • "peerMatches": [
    ],
  • "summary": {
    }
}

Slip

Bank slip upload and auto-match operations (peer-matching)

Upload a bank-slip image (peer-matching)

Uploads a bank-slip image to reconcile a pending peer-matching PayIn transaction. The upload is multipart/form-data with a single image file field.

Processing steps (server-side):

  1. Store the image and create a bank_slip_image record with status PENDING plus an associated transaction_evidence record.
  2. If slip verification is enabled, extract the QR from the slip and call the slip-verify provider to get the decoded transfer data.
  3. Reject duplicate slips (same verified slip hash) — returns DUP MATCHED and the evidence is rejected.
  4. Validate that the decoded destination account matches one of the company's configured destination accounts (skipped for peer-only companies where destinations are dynamic).
  5. Attempt to auto-match the slip to a pending BankExpect for the company using amount + peerBank + peerAccountNumber + time window. On a unique match, the corresponding PayIn transaction is confirmed and a status callback is scheduled.

Auto-match outcomes (recorded on the evidence / returned via later status):

  • MATCHED <externalRefId> — slip bound to a pending transaction
  • NOT MATCH - NO TXCANDIDATE FOUND
  • NOT MATCH - MULTIPLE TXCANDIDATE FOUND
  • NOT MATCH - OUT OF TIME (N hours ago) — slip timestamp older than 24h
  • NOT MATCH - ALREADY MATCHED <externalRefId>
  • NOT MATCH - REFUNDED

The HTTP response returns the stored image(s) metadata. The final match result is reported asynchronously via POST /status/{refId} or the status callback.

Authorizations:
ApiKeyAuth
Request Body schema: multipart/form-data
required
image
required
string <binary>

Bank slip image file (PNG / JPEG / WebP). Sent as multipart/form-data field image.

Responses

Response samples

Content type
application/json
[]

Status

Transaction status operations

Trigger status callback by refId

Manually triggers a status callback for a transaction using the internal reference ID (refId)

Authorizations:
ApiKeyAuth
path Parameters
refId
required
string

Internal transaction reference ID

Responses

Response samples

Content type
application/json
{
  • "status": 0,
  • "message": "Success",
  • "success": true,
  • "code": 0
}

Trigger status callback by externalRefId

Manually triggers a status callback for a transaction using the external reference ID (externalRefId provided by company)

Authorizations:
ApiKeyAuth
path Parameters
externalRefId
required
string

External transaction reference ID (provided by company)

Responses

Response samples

Content type
application/json
{
  • "status": 0,
  • "message": "Success",
  • "success": true,
  • "code": 0
}

Get transaction status by refId

Retrieves the current status of a transaction using the internal reference ID (refId)

Authorizations:
ApiKeyAuth
path Parameters
refId
required
string

Internal transaction reference ID

Responses

Response samples

Content type
application/json
{
  • "status": 0,
  • "message": "Success",
  • "success": true,
  • "code": 0,
  • "transaction": {
    }
}

Get transaction status by externalRefId

Retrieves the current status of a transaction using the external reference ID (externalRefId provided by company)

Authorizations:
ApiKeyAuth
path Parameters
externalRefId
required
string

External transaction reference ID (provided by company)

Responses

Response samples

Content type
application/json
{
  • "status": 0,
  • "message": "Success",
  • "success": true,
  • "code": 0,
  • "transaction": {
    }
}

PeerMatches

Peer-match fulfillment details for PayOut (withdraw) transactions

Get peer-match fulfillment details for a PayOut (by refId)

Returns the peer-match fulfillment state for a peer-matching PayOut (withdraw) transaction, identified by internal refId.

Use this when you need to see which peer deposits are being used to fulfill a withdraw order and how much is still outstanding. The regular POST /status/{refId} endpoint only returns top-level transaction state; this endpoint adds the underlying PeerWithdrawRequest accounting (amount / matchedAmount / pendingAmount / remainingAmount) plus each PeerToPeerMatch that has been attached so far.

Accounting semantics:

  • amount — total amount the platform is trying to fulfill from peers
  • matchedAmount — amount already confirmed by peer deposits
  • pendingAmount — amount locked to PENDING matches awaiting slip confirmation
  • remainingAmount = amount - matchedAmount - pendingAmount

Match statuses (peerMatches[].status):

  • PENDING — deposit reserved against this withdraw, awaiting slip confirmation
  • CONFIRMED — deposit slip verified, amount added to matchedAmount
  • REJECTED — match cancelled (e.g. slip verification failed)
  • EXPIRED — slip window passed without confirmation; amount released

Withdraw request statuses (peerWithdrawRequest.status):

  • OPEN — still accepting new matches
  • FULFILLED — fully covered (no more matches will be created)
  • STALE — expired without full fulfillment

Privacy / compactness: Only the destination account (destBank / destAccountNumber) the peer depositor paid to, and a signed, time-limited slipImageUrl pointing at the uploaded slip, are returned per match. Counterparty (depositor) identifiers, member info, the raw slip QR string, and the full slip-verify JSON are intentionally omitted — this endpoint is designed to be compact and cheap to poll; use the admin peer-monitor API for full forensics.

Authorizations:
ApiKeyAuth
path Parameters
refId
required
string

Internal transaction reference ID of the PayOut

Responses

Response samples

Content type
application/json
{
  • "status": 0,
  • "message": "Success",
  • "success": true,
  • "code": 0,
  • "refId": "TX-20260422-001",
  • "externalRefId": "WITHDRAW-12345",
  • "transactionStatus": "PROCESSING",
  • "requestedAmount": "5000.00",
  • "amount": "5000.00",
  • "peerWithdrawRequest": {
    },
  • "peerMatches": [
    ],
  • "summary": {
    }
}

Get peer-match fulfillment details for a PayOut (by externalRefId)

Same as POST /payOut/peerMatches/{refId} but looks up the PayOut by the external reference ID originally provided by the company when the transaction was created.

Authorizations:
ApiKeyAuth
path Parameters
externalRefId
required
string

External transaction reference ID (provided by company) of the PayOut

Responses

Response samples

Content type
application/json
{
  • "status": 0,
  • "message": "Success",
  • "success": true,
  • "code": 0,
  • "refId": "TX-20260422-001",
  • "externalRefId": "WITHDRAW-12345",
  • "transactionStatus": "PROCESSING",
  • "requestedAmount": "5000.00",
  • "amount": "5000.00",
  • "peerWithdrawRequest": {
    },
  • "peerMatches": [
    ],
  • "summary": {
    }
}

Account

Account information operations

Get account balances

Retrieves balance information for all active company accounts (PAYIN and PAYOUT)

Authorizations:
ApiKeyAuth

Responses

Response samples

Content type
application/json
{
  • "status": 0,
  • "message": "Success",
  • "success": true,
  • "code": 0,
  • "accounts": [
    ]
}

Get supported bank codes

Retrieves the list of supported bank codes for transactions

Authorizations:
ApiKeyAuth

Responses

Response samples

Content type
application/json
{
  • "status": 0,
  • "message": "Success",
  • "success": true,
  • "code": 0,
  • "data": [
    ]
}