Skip to main content

Your First Collection

In this tutorial you'll accept your first QRPH payment — from creating a Payment Intent, to displaying the QR code, to confirming the payment was received.

Time: ~15 minutes Prerequisites: Sandbox credentials from /sandbox. A merchant and account already set up (follow Your First API Call first).


What you'll build

By the end of this tutorial you'll have:

  1. Created a Payment Intent for a specific amount
  2. Displayed a QRPH code
  3. Simulated a payment in sandbox
  4. Confirmed the payment via a webhook event

Step 1: Create a Payment Intent

A Payment Intent represents a single payment request. Specify the account to receive funds and the amount to collect.

curl -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
-X POST "https://api.partners.nextpay.world/v2/payment-intents" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: tutorial-collection-001" \
-d '{
"account_id": "acct_01HXYZ",
"amount": 25000,
"description": "Tutorial test payment",
"external_id": "tutorial-collection-001"
}'

Response

{
"id": "pi_01HABC",
"account_id": "acct_01HXYZ",
"amount": 25000,
"status": "pending",
"description": "Tutorial test payment",
"external_id": "tutorial-collection-001",
"qr_code": "00020101021226...",
"qr_image_url": "https://api.partners.nextpay.world/v2/payment-intents/pi_01HABC/qr",
"created_at": "2025-11-15T10:00:00Z",
"expires_at": "2025-11-15T11:00:00Z"
}

Step 2: Display the QR code

In a real application, you'd show qr_image_url in your checkout UI. For this tutorial, open the URL in a browser to see the QR code image.

<!-- In your app's checkout page -->
<div class="checkout-qr">
<p>Scan to pay PHP 250.00</p>
<img
src="https://api.partners.nextpay.world/v2/payment-intents/pi_01HABC/qr"
alt="QRPH payment code"
width="256"
height="256"
/>
<p>Expires in 60 minutes</p>
</div>

The QR code works with all QRPH-compatible apps: GCash, Maya, BPI, BDO, UnionBank, and any bank app that supports QR Ph.


Step 3: Simulate a payment (sandbox)

In the sandbox, you can simulate a payment by calling the sandbox payment simulation endpoint:

curl -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
-X POST "https://api.partners.nextpay.world/v2/sandbox/simulate-payment" \
-H "Content-Type: application/json" \
-d '{
"payment_intent_id": "pi_01HABC",
"amount": 25000
}'

In production, the customer scans and pays via their bank app — you don't control this step.


Step 4: Check the Payment Intent status

After the simulated payment, check the status:

curl -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
"https://api.partners.nextpay.world/v2/payment-intents/pi_01HABC"
{
"id": "pi_01HABC",
"status": "paid",
"amount": 25000,
"paid_at": "2025-11-15T10:05:33Z"
}

Step 5: Set up webhook handling (production)

In production, don't poll — handle the payment_intent.paid webhook instead:

app.post('/webhooks/nextapi', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-nextpay-signature'];

if (!verifySignature(req.body, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Unauthorized');
}

const event = JSON.parse(req.body);

if (event.type === 'payment_intent.paid') {
const { external_id, amount } = event.data;
// external_id is what you set: "tutorial-collection-001"
console.log(`Payment received for ${external_id}: PHP ${amount / 100}`);
// Mark the order as paid in your system
}

res.status(200).send('OK');
});

external_id is the key: set it to your internal order or invoice reference when creating the Payment Intent, and it comes back in the webhook — no need to store a mapping from Payment Intent IDs to your records.


Payment Intent lifecycle

pending → paid        (customer pays successfully)
pending → expired (1 hour passes without payment)
pending → cancelled (you call POST /cancel)

Once paid, the funds are credited to the account immediately. You can check the account balance to confirm:

curl -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
"https://api.partners.nextpay.world/v2/accounts/acct_01HXYZ/balances"

What's next?