AUTOXSPARE Docs
API reference

Curlec

The payment provider — amounts, signatures, and the FPX redirect.

apps/backend/src/modules/curlec/ — registered as a provider of the payment module, not as a module of its own. Provider identifier: curlec.

Curlec is Razorpay's Malaysian entity, so the API is Razorpay's, and the Razorpay docs apply.

Flow

Order-first, which is Razorpay's model:

  1. Backend creates a Curlec order for the cart total.
  2. Storefront opens Curlec's checkout with that order id.
  3. Customer pays by FPX or card.
  4. Curlec returns the customer to the return route.
  5. Storefront sends the signed receipt to POST /store/curlec/return to resolve the original cart without cookies.
  6. Core cart completion authorizes the existing session by querying Curlec for a matching paid order. A completed cart returns the same order on retry.

POST /store/curlec/return

Requires the publishable API key. Body fields: razorpay_order_id, razorpay_payment_id, razorpay_signature (64 lowercase hex characters). Returns { "cart_id": "cart_..." } after verifying the receipt and original session's provider, amount and MYR currency. Malformed requests return 400; invalid signatures 401; missing associations 404; session mismatches 409; missing gateway configuration 503. This endpoint does not charge or create an order.

The storefront next calls core cart completion. Neither path changes cookie security or trusts a client-supplied cart id from the bank callback.

Amounts

Sent in sen — multiply by 100.

const amount = Math.round(cartTotal * 100)

The provider refuses any currency other than MYR outright. Curlec settles MYR only, and an amount in another currency would be passed through as sen regardless, charging a number that means nothing.

Signature verification

HMAC-SHA256 over order_id|payment_id, keyed with the API key secret:

const expected = crypto
  .createHmac("sha256", secret)
  .update(`${razorpay_order_id}|${razorpay_payment_id}`)
  .digest("hex")

Compare against razorpay_signature. A mismatch means the callback is not trustworthy and the payment must not be authorised.

FPX needs a redirect

FPX will not work as an inline modal. The checkout must be opened with:

{ callback_url: "<return route>", redirect: true }

The storefront's payment button loads https://checkout.razorpay.com/v1/checkout.js on demand and deliberately passes no handler — FPX redirects away, so a handler callback never fires and relying on it silently loses the payment.

Test and live

CURLEC_MODE selects which key pair medusa-config.ts reads:

const CURLEC_MODE = (process.env.CURLEC_MODE ?? 'live') as 'test' | 'live'
const curlecKeyId = (CURLEC_MODE === 'live'
  ? process.env.CURLEC_LIVE_KEY_ID
  : process.env.CURLEC_TEST_KEY_ID) ?? process.env.CURLEC_KEY_ID

Currently live. Changing it is a Railway variable change plus a redeploy.

Webhook

Configured in the Curlec dashboard against the backend. The signing secret is separate from the API key secret — verification uses the webhook secret.

On this page