Guides
Express Integration
Integrate StableOps payment processing into your Express application.
Install
pnpm add @stableops/api-sdk expressConfigure
STABLEOPS_API_KEY=sk_sandbox_...
STABLEOPS_WEBHOOK_SECRET=whsec_...
STABLEOPS_API_URL=https://api.stableops.devCreate lib/stableops.js:
const { StableOps } = require('@stableops/api-sdk')
const stableops = new StableOps({
apiKey: process.env.STABLEOPS_API_KEY,
environment: 'sandbox',
baseUrl: process.env.STABLEOPS_API_URL,
})
module.exports = { stableops }Create payment orders
const express = require('express')
const { stableops } = require('../lib/stableops')
const router = express.Router()
router.post('/orders', async (req, res) => {
try {
const { merchantOrderId, amount, metadata } = req.body
if (!merchantOrderId || !amount) {
return res
.status(400)
.json({ error: 'merchantOrderId and amount are required' })
}
const order = await stableops.paymentOrders.create(
{
merchantOrderId,
amount: String(amount),
settlementAsset: 'USDC',
acceptedAssets: [{ chain: 'base', asset: 'USDC' }],
expiresAt: new Date(Date.now() + 30 * 60 * 1000).toISOString(),
metadata: metadata || {},
},
{ idempotencyKey: merchantOrderId },
)
res.status(201).json({
id: order.id,
merchantOrderId: order.merchantOrderId,
amount: order.amount,
status: order.status,
paymentInstructions: order.paymentInstructions,
})
} catch (error) {
res.status(error.status || 500).json({
error: error.code || 'stableops_error',
message: error.message,
})
}
})
router.get('/orders/:id', async (req, res) => {
const order = await stableops.paymentOrders.retrieve(req.params.id)
res.json(order)
})
router.post('/orders/:id/cancel', async (req, res) => {
const order = await stableops.paymentOrders.cancel(req.params.id)
res.json(order)
})
module.exports = routerVerify webhooks
Mount the webhook route before express.json() so the handler receives the raw
body used for signing.
const express = require('express')
const { SIGNATURE_HEADER, verifySignature } = require('@stableops/api-sdk/webhooks')
const router = express.Router()
const WEBHOOK_SECRET = process.env.STABLEOPS_WEBHOOK_SECRET
router.post(
'/stableops',
express.raw({ type: 'application/json' }),
async (req, res) => {
const rawBody = req.body.toString('utf8')
const result = verifySignature({
secrets: [WEBHOOK_SECRET],
header: req.header(SIGNATURE_HEADER),
rawBody,
})
if (!result.ok) {
return res.status(400).json({ error: result.reason })
}
const event = JSON.parse(rawBody)
switch (event.type) {
case 'payment.finalized':
// Fulfill event.data.payment_order_id after deduping X-Event-Id.
break
case 'payment.reverted':
case 'payment.expired':
break
}
res.sendStatus(200)
},
)
module.exports = router