Best Practices
Integration patterns and production recommendations.
Production patterns for reliable BSync integrations.
Timeouts
Set explicit timeouts to prevent hung requests:
const bsync = new BSync({
apiKey: process.env.BSYNC_API_KEY,
timeout: 30_000,
});For user-facing checkout creation, consider a shorter timeout (10–15s) and show a friendly error if the API is slow.
Retries
Let the SDK handle retries for network errors, 5xx, and 429:
const bsync = new BSync({
apiKey: process.env.BSYNC_API_KEY,
maxRetries: 2,
});Do not implement your own retry loop around SDK calls — this can bypass idempotency key stability. If you need custom retry logic, pass a stable idempotencyKey.
For webhooks, return 200 immediately and retry processing internally if needed.
Logging
Log these fields on every API interaction:
| Field | Source | Purpose |
|---|---|---|
requestId | Error responses | Support tickets, debugging |
paymentId | Payment responses | Order tracking |
eventId | Webhook payloads | Deduplication, audit |
externalOrderId | Your order reference | Business correlation |
catch (error) {
logger.error("BSync API error", {
code: error.code,
requestId: error.requestId,
statusCode: error.statusCode,
});
}Never log API keys or webhook secrets.
Correlation IDs
Tie BSync identifiers to your internal order system:
await bsync.paymentIntents.create({
amount: 150,
currency: "EGP",
externalOrderId: order.id,
metadata: { orderId: order.id, customerId: customer.id },
});When debugging, search by externalOrderId in both your system and BSync Dashboard.
Webhook Queue
Never process webhooks synchronously in the HTTP handler:
app.post("/webhooks/bsync", express.raw({ type: "application/json" }), (req, res) => {
if (!verifySignature({ ... })) {
return res.status(401).send("Invalid signature");
}
const event = JSON.parse(req.body.toString());
if (await isDuplicate(event.eventId)) {
return res.status(200).json({ received: true });
}
await queue.add("process-webhook", event);
res.status(200).json({ received: true });
});Process from the queue with retries, dead-letter handling, and monitoring.
Never Trust the Frontend
Payment confirmation must happen server-side:
❌ Customer redirected to successUrl → fulfill order
✅ PAYMENT_PAID webhook verified → getStatus() === "paid" → fulfill order
The successUrl redirect is a UX convenience, not a payment confirmation.
Idempotency
- Use
order-${orderId}as the idempotency key for payment creation. - Deduplicate webhooks by
eventId. - Check order fulfillment status before processing — avoid double fulfillment.
Key Security
- API keys in environment variables or secrets manager only.
- Separate keys per service (checkout, webhooks, admin).
- Rotate keys quarterly or after any team member departure.
- Never expose keys in client-side code, logs, or error messages.
Least Privilege
Create API keys with only the permissions each service needs. A webhook processor does not need payment creation permissions.
Error Handling
Handle every error type explicitly:
if (error instanceof ValidationError) { /* fix request */ }
else if (error instanceof AuthenticationError) { /* alert ops */ }
else if (error instanceof RateLimitError) { /* backoff */ }
else if (error instanceof BSyncConnectionError) { /* network issue */ }Never swallow errors silently.
Related
Was this page helpful?
best-practices