Recipes
Node (Express) — create session and redirect host
const express = require('express');
const app = express();
app.use(express.json());
app.post('/api/checkout/payshare', async (req, res) => {
const { amount, successUrl, cancelUrl, orderRef } = req.body;
const r = await fetch('https://app.payshare.nz/api/v1/payments/sessions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-PayShare-API-Key': process.env.PAYSHARE_API_KEY,
},
body: JSON.stringify({
integrationId: process.env.PAYSHARE_INTEGRATION_ID,
amount: Number(amount),
currency: 'NZD',
unconfigured: true,
successUrl,
cancelUrl,
merchantOrderRef: orderRef,
}),
});
const data = await r.json();
if (!data.success) return res.status(400).json(data);
const payUrl = `https://app.payshare.nz/pay/${data.sessionId}?host=1&hostToken=${data.hostToken}`;
res.json({ payUrl, sessionId: data.sessionId });
});Next.js — “Pay with PayShare” button
Use the API route (or a server action) to call POST /api/v1/payments/sessions with the cart total, then redirect to the returned payUrl, or open it in a new tab. Do not send the API key from the browser; only the redirect URL is safe to use client-side.
Webhook verification (Node)
Build canonical JSON (keys sorted), then:
const crypto = require('crypto');
function verify(body, signature, secret) {
const canonical = JSON.stringify(sortKeys(body));
const expected = crypto.createHmac('sha256', secret).update(canonical).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(signature, 'hex'));
}Respond 200 only after verification and idempotency check.