Skip to main content

Journey 2: Part payment

You prove that your app handles PART_PAID: it shows the remaining amount, and you can either collect the rest or credit the part that arrived.

In production you do not control how much a payer sends. A bank transfer can be short, and a crypto transfer can arrive smaller than the quote.

You need

  • Before you start done.
  • A new payment request for 1000.00 in UNPAID status, with the method you will test in paymentMethods.

Step 1: Pay part of the amount

Send POST /v1/collections/simulate-payment with an amount smaller than the total:

{
"paymentRequestReference": "ORDER-10045",
"paymentMethod": "BANK_TRANSFER",
"amount": 400.00
}

Expect paymentRequestStatus: "PART_PAID". The 400 is completed and credited to your wallet at once. Your webhook receives payment.pending, payment.received, payment_request.part_paid and payment.completed.

Step 2: Choose what happens next

2a. Collect the rest. Send the same call without amount. Hubpay pays the remaining 600. Expect paymentRequestStatus: "PAID" and payment_request.paid.

2b. Keep what arrived. Do nothing. The 400 is already in your wallet and the request stays PART_PAID. Decide in your app: remind the payer, adjust the invoice, or wait.

Step 3: Prove it

CheckAfter 2aAfter 2b
GET /v1/collections/payment-requests/{id}status: "PAID", amountRemaining: 0status: "PART_PAID", amountRemaining greater than 0, payment COMPLETE
Your webhook URLpayment_request.part_paid, then payment_request.paidpayment_request.part_paid, then payment.completed
Your appShows the order as paidShows the amount still due and the amount received. For bank transfer and crypto, that amount is in your wallet.

Payout: if the request has payoutDetails, each completed payment can create a payout for its amount. Check it as in Journey 1, step 4.

If it does not work

ProblemCauseFix
400 on simulate-paymentamount is larger than the remaining balance, or you changed the methodUse a smaller amount and the same method
400 on completion in 2bThe quote is still openExpire it with CRYPTO_QUOTE first
400 on completionA deposit is still PENDINGSend it again with "status": "CONFIRMED" and the same depositId
Second deposit rejectedCheckout created a new quote between callsIf the first quote is still open, send its quotePaymentId from the first response to select it. If it has expired, follow 2b to credit the first deposit, then take a new quote for the rest

Good to know

  • percentageOfRemaining is a share of what is left on the quote. Sending 50 twice with new deposit IDs pays 50%, then 25%.
  • The total of pending and confirmed deposits cannot exceed the quote amount. Overpayment cannot be simulated.
  • Expiring a quote does not expire the payment request. For that, see Journey 3.