Developer Docs

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:

FieldSourcePurpose
requestIdError responsesSupport tickets, debugging
paymentIdPayment responsesOrder tracking
eventIdWebhook payloadsDeduplication, audit
externalOrderIdYour order referenceBusiness 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.

Was this page helpful?

best-practices

Command Palette

Search for a command to run...