Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.paybridgenp.com/llms.txt

Use this file to discover all available pages before exploring further.

PayBridge NP follows the Stripe model: a coupon is a reusable discount template; a promotion code is a customer-facing string (like LAUNCH50) that redeems a coupon.

Concepts

Coupon

The template. Defines:
  • Discount: percent (1-100%) or amount (fixed paisa off)
  • Duration: once (next invoice only), repeating (N cycles), or forever
  • Caps: maxRedemptions, redeemBy expiry
  • Scope: appliesToPlanIds (nullable = all plans)

Promotion code

A public handle pointing at a coupon. Has its own independent limits:
  • maxRedemptions
  • expiresAt
  • minimumAmount (order floor in paisa)
  • customerIds restriction
A single coupon can have multiple promotion codes - useful for split tests (LAUNCH50 vs WELCOME50).

Creating a coupon

curl -X POST https://api.paybridgenp.com/v1/billing/coupons \
  -H "Authorization: Bearer sk_sandbox_…" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "LAUNCH",
    "name": "Launch discount",
    "discountType": "percent",
    "percentOff": 50,
    "duration": "repeating",
    "durationInCycles": 3,
    "maxRedemptions": 100
  }'

Creating a promotion code

curl -X POST https://api.paybridgenp.com/v1/billing/promotion-codes \
  -H "Authorization: Bearer sk_sandbox_…" \
  -H "Content-Type: application/json" \
  -d '{
    "couponId": "cpn_…",
    "code": "LAUNCH50",
    "maxRedemptions": 500,
    "expiresAt": "2026-06-01T00:00:00Z"
  }'
The code is case-insensitive and stored uppercase.

Validating before applying

curl -X POST https://api.paybridgenp.com/v1/billing/promotion-codes/validate \
  -H "Authorization: Bearer sk_test_…" \
  -H "Content-Type: application/json" \
  -d '{"code":"LAUNCH50","planId":"bp_…","amount":50000}'
Returns { valid: true, coupon, promotion_code, discount_preview } or { valid: false, reason }. This is read-only and safe to call on every keystroke.

Applying to a subscription

curl -X POST https://api.paybridgenp.com/v1/billing/subscriptions/sub_…/apply-coupon \
  -H "Authorization: Bearer sk_sandbox_…" \
  -H "Content-Type: application/json" \
  -d '{"promotionCode":"LAUNCH50"}'
Or pass couponId directly to skip the promo-code layer.

Duration semantics

DurationBehavior
onceApplied to the next invoice only. Deactivated after.
repeatingApplied for the next durationInCycles invoices. Auto-deactivates.
foreverApplied to every future invoice until the coupon is deactivated.

Redemption atomicity

Coupon and promotion code counters update inside the same UPDATE ... WHERE redeemedCount < maxRedemptions statement - so racing N parallel applies can never exceed the cap. The N-1 losers get an error like "Promotion code redemption limit reached". Safe to retry.

Invoice math

When an invoice is generated for a subscription with an active discount, PayBridge emits line items in this order:
  1. Subscription (plan amount, positive)
  2. Discount (coupon discount, negative)
  3. Tax (VAT on the discounted amount - see Tax guide)
Totals follow Stripe defaults (discount before tax):
subtotal       = subscription line amount
discount_total = |discount line amount|
taxable_base   = max(0, subtotal - discount_total)
tax_total      = round(taxable_base * rate_bps / 10000)
amount_due     = subtotal - discount_total + tax_total

Webhooks

EventWhen it fires
coupon.createdCoupon created via API or dashboard.
coupon.updated / coupon.deletedDeactivation.
coupon.expiredDaily sweep deactivates expired coupons.
promotion_code.redeemedEach time a promo is applied to a subscription.
subscription.discount_appliedCoupon attached to a sub.
subscription.discount_removedActive discount cleared.

Disabling the feature

Set COUPONS_ENABLED=false in the API environment. Coupon CRUD returns 404; invoice generation skips the discount + tax line items (existing subs produce identical invoices to pre-Phase-2).