A gift card Commerce App is a fullstack extension that integrates Salesforce B2C Commerce with an external gift-card processor. The app implements the sfcc.app.giftcard.* hooks and ships Storefront Next components that collect the card and show the applied amount.
Gift cards are a fullstack domain with a platform-defined adapter interface. The platform owns checkout orchestration, Shopper API contracts, amount allocation, and fail-and-reopen. The app owns the processor calls (balance, capture, and refund) and the UI targets those calls surface through. Declare "domain": "giftcard" in commerce-app.json.
Gift Cards Commerce Apps is a beta service that is subject to the Beta Services Terms at Agreements - Salesforce.com or a written Unified Pilot Agreement if executed by Customer, and applicable terms in the Product Terms Directory. Use of this beta service is at the Customer’s sole discretion.
A gift card Commerce App runs only when the feature toggle PaymentFraudOrchestrationEnabled is on. Contact Salesforce Customer Support to enable it.
Note
These hooks run only for an installed Gift Cards Commerce App. Register them from the app cartridge. Native gift certificates (GIFT_CERTIFICATE) continue on their own payment method and never call these hooks.
The payment method ID for Commerce App gift cards is COMMERCE_APP_GIFT_CARD. Payment apps don’t claim it.
Architecture
A gift card Commerce App has two moving parts:
Server-side hooks in a cartridge. These implement sfcc.app.giftcard.getBalance, sfcc.app.giftcard.capture, and sfcc.app.giftcard.refund.
Extension components in the Storefront Next storefront. These render the apply form, the applied-card line in the order summary, and gift-card management on My Account. Map them in target-config.json. See Gift Card UI Targets.
The shopper enters the card in the storefront extension. The extension calls Shopper Payments for a balance lookup and Shopper Baskets to apply the card. When the order’s payment is processed, the platform captures the gift card before it authorizes any other claimed payment method. If a later step fails, the platform asks the app to refund.
The platform stores only the last four digits of the gift-card number on the payment instrument. Full card number, PIN, and CVC stay off the instrument, payment details, and transaction details.
What Merchants Get
After a Gift Cards Commerce App is installed and configured on the site:
Shoppers can look up a card balance and apply one Commerce App gift card to a basket.
The platform allocates how much of that card to use. Shoppers can request a smaller amount. Shoppers can’t request more than the card balance.
When the order’s payment is processed, the platform captures the gift card before it authorizes any other claimed payment method, such as a credit card covering the remainder.
If capture fails, or a later payment or fraud step fails after a successful capture, the platform fails the order and reopens the basket. The platform then asks the app to refund any captured gift-card funds.
Registration of sfcc.app.giftcard.getBalance controls feature availability. When that hook is absent, balance lookup and apply fail.
Shopper APIs
Look up balance
POST /checkout/shopper-payments/v1/organizations/{organizationId}/payment-instruments/balance
Pass paymentMethodIdCOMMERCE_APP_GIFT_CARD, an optional provider, and providerProperties (card number and any app-specific fields). The platform forwards these to getBalance unchanged. The call doesn’t create a basket or order payment instrument.
Any other paymentMethodId, or a hook that is missing or returns no balance, fails with “Payment instrument balance isn’t implemented.” Apply fails the same way when the hook is missing or returns no balance.
Apply a gift card to a basket
POST /checkout/shopper-baskets/v1/organizations/{organizationId}/baskets/{basketId}/payment-instruments
giftCardRequest.brand, giftCardRequest.cardNumber, and giftCardRequest.cvc are required. The platform passes the request to getBalance unchanged.
amount is optional. When it is omitted, the platform applies as much of the card as the unpaid remainder allows.
When amount is present, it must be greater than or equal to zero and must not exceed the hook balance. The allocated amount is min(shopper amount, unpaid remainder).
Unpaid remainder is the basket grand total minus amounts already on other payment instruments.
The gift-card currency must match the basket currency.
The platform rejects a second gift card on the same basket. Remove the existing instrument before applying another.
Apply calls sfcc.app.giftcard.getBalance. It doesn’t capture funds. On success the basket payment instrument uses method COMMERCE_APP_GIFT_CARD and stores last4 only.
Update a gift-card instrument
PATCH of a basket gift-card payment instrument can’t change the amount or replace stored credentials. To change the amount or the card, remove the instrument and apply again.
Place the order
POST /checkout/shopper-orders/v1/organizations/{organizationId}/orders with the basket ID creates the order in CREATED. Gift card capture doesn’t run yet. It runs inside attemptOrderTransition. The payment app’s storefront extension creates the order and then calls the app’s order transition endpoint, which calls OrderMgr.attemptOrderTransition. It does this even when gift cards pay the whole order, so a gift card app depends on an installed payment app to complete checkout. A SCAPI POST or PATCH of a claimed order payment instrument also runs attemptOrderTransition. See Payment Commerce Apps.
Clients must inspect status on HTTP 200. A gift-card capture failure, or a later payment failure after capture, returns HTTP 200 with "status": "failed" from the call that ran attemptOrderTransition. The platform persists the order as failed, releases inventory and coupons, and reopens the basket. The shopper can update that basket and submit again.
Treat HTTP 200 with status: failed as a failed checkout.
Apply validation
The apply endpoint rejects the following situations:
Situation
HTTP
Typical detail
Negative shopper amount
400
The payment instrument amount must not be negative.
Shopper amount greater than card balance
400
Gift card apply amount exceeds the available card balance.
Gift card already on the basket
400
Gift card is already applied to this basket. Remove it before applying again.
Missing giftCardRequest, brand, cardNumber, or cvc
400
Gift card application requires gift card credentials.
Hook balance currency differs from basket currency
400
Gift card balance currency ‘{balance}’ doesn’t match basket currency ‘{basket}’.
Hook balance is zero or negative
400
Gift card balance must be greater than zero.
PATCH amount on an existing gift-card instrument
400
Gift card update doesn’t accept a shopper-provided amount.
PATCH that tries to replace credentials
400
Gift card update can’t replace stored credentials.
getBalance throws
500
Checkout cannot proceed with an unknown balance.
Basket grand total is unavailable
500
Internal server error (apply is aborted).
Checkout Lifecycle
Gift-card capture is synchronous and runs before card authorization, so a declined gift card never leaves a live card authorization that needs reversing.
1sequenceDiagram2 participant Shopper3 participant Platform4 participant GiftCardApp as Gift Card App5 participant PaymentApp as Payment App6 participant FraudApp as Fraud App78 Shopper->>Platform: POST payment-instruments/balance9 Platform->>GiftCardApp: sfcc.app.giftcard.getBalance10 GiftCardApp-->>Platform: Money11 Platform-->>Shopper: balance (no PI created)1213 Shopper->>Platform: POST baskets/.../payment-instruments14 Platform->>GiftCardApp: sfcc.app.giftcard.getBalance15 GiftCardApp-->>Platform: Money16 Platform->>Platform: allocate min(optional amount, unpaid remainder)17 Platform-->>Shopper: basket PI COMMERCE_APP_GIFT_CARD1819 Shopper->>Platform: POST /orders20 opt Fraud app installed21 Platform->>FraudApp: sfcc.app.fraud.beforePayment22 end23 Platform->>Platform: convert basket to CREATED order24 Platform-->>Shopper: HTTP 200, status created2526 Shopper->>Platform: Order transition endpoint (payment app extension), or PATCH order payment instrument27 Platform->>Platform: attemptOrderTransition28 Platform->>GiftCardApp: sfcc.app.giftcard.capture29 alt Capture fails30 Platform->>Platform: fail order, reopen basket31 Platform->>GiftCardApp: sfcc.app.giftcard.refund (best effort)32 Platform-->>Shopper: HTTP 200, status failed33 else Capture succeeds34 Platform->>PaymentApp: sfcc.app.payment.onAttemptOrderTransition35 opt Payment complete and fraud app installed36 Platform->>FraudApp: sfcc.app.fraud.afterPayment37 end38 alt Later payment or fraud fails39 Platform->>PaymentApp: sfcc.app.payment.reversePayment40 Platform->>GiftCardApp: sfcc.app.giftcard.refund (best effort)41 Platform-->>Shopper: HTTP 200, status failed42 else Success43 Platform-->>Shopper: HTTP 200, placed order44 end45 end
When capture returns an error, the platform skips remaining gift-card captures and fails the order.
Capture and refund each run only when the app registered that hook. A missing capture hook skips capture. A missing refund hook counts as success. The platform skips capture for any gift-card instrument whose transaction is no longer in the created state, so a retry never re-captures. After a capture call, the platform marks the instrument pending.
Gift-card instruments don’t run payment app hooks. Gift cards use the dedicated sfcc.app.giftcard.* hooks.
Fail, Cancel, and Refund
sfcc.app.giftcard.refund runs inside fail-order and cancel-order, after sfcc.app.payment.reversePayment, for each gift-card instrument.
Refund is best effort. The platform logs a refund error, and fail or cancel continues. The shopper still receives a failed or canceled order. Make refund idempotent so a retry doesn’t double-credit the card. Return Status.ERROR when the processor rejects the refund or the outcome is unknown, so the failed order keeps a record. Return Status.OK only after the processor has refunded.
Extension Points
Register your hooks. In the app site cartridge, add a package.json that points to hooks.json. Script paths in hooks.json are relative to that file. Each exported function name must match the last segment of its extension point.
For signatures and the latest class details, see the Script API reference for dw.apps.checkout.hooks.GiftCardHooks.
Hook
When
Returns
Failure
sfcc.app.giftcard.getBalance
Balance endpoint and basket apply
dw.value.Money
Throw → HTTP 500. An unknown balance is never treated as zero.
sfcc.app.giftcard.capture
attemptOrderTransition, before claimed payment onAttemptOrderTransition
dw.system.Status
Status.ERROR, null, or a throw fails the order and reopens the basket.
sfcc.app.giftcard.refund
Fail-order and cancel-order, after payment reverse
dw.system.Status
Logged. Fail or cancel continues. null is treated as success.
sfcc.app.giftcard.getBalance
getBalance(request). Read-only. Don’t authorize, capture, or persist a payment instrument.
Return a positive available dw.value.Money in the basket currency. The platform rejects zero balances, negative balances, and currency mismatches on apply, but a balance lookup can still return that Money.
The request argument is the Shopper Payments balance body (PaymentInstrumentBalanceRequestWO), including providerProperties. On apply, the platform passes giftCardRequest through to this hook unchanged.
sfcc.app.giftcard.capture
capture(order, paymentInstrument). Capture the allocated amount on the order payment instrument (the amount the platform stored at apply). Don’t compute a new balance inside capture.
Return Status.OK only after the processor has captured, or when the call is safely idempotent for this instrument. Before returning Status.OK, set the payment transaction status to PaymentTransaction.STATUS_CAPTURED.
Return Status.ERROR for a declined or failed capture. null is a contract failure and fails the order.
Repeated calls for the same order payment instrument must be safe.
Capture and refund run with the session switched to the order’s site.
When the processor approves a different amount than the allocated instrument amount, treat that as capture failure. Return Status.ERROR so the platform fails the order and refunds. Don’t accept a partial capture.
sfcc.app.giftcard.refund
refund(order, paymentInstrument). Refund a settled capture when the order is failed or canceled after funds were taken.
Make repeated calls for the same instrument safe.
Prefer returning Status.OK or Status.ERROR. The platform logs a throw and continues the fail or cancel.
Data Model
Write processor outcome onto the payment transaction before returning an error status, so the failed order keeps a durable record.
1var GiftCardTransactionDetails = require('dw/extensions/payments/GiftCardTransactionDetails');2var PaymentTransaction = require('dw/order/PaymentTransaction');3var Status = require('dw/system/Status');45exports.capture = function(order, paymentInstrument){6 var txn = paymentInstrument.getPaymentTransaction();7 var result = ExampleGiftCard.capture(txn.getAmount());89 var details = new GiftCardTransactionDetails();10 if(result.approved && result.amountMatchesAllocation){11 details.setApprovalCode(result.approvalCode);12 txn.setTransactionDetails(details);13 txn.setStatus(PaymentTransaction.STATUS_CAPTURED);14 return new Status(Status.OK);15}1617 details.setReasonCode(GiftCardTransactionDetails.REASON_CODE_DECLINED);18 details.setProcessorResponseText(result.processorText);19 txn.setTransactionDetails(details);20 return new Status(Status.ERROR);21};
GiftCardTransactionDetails
Set it through paymentInstrument.getPaymentTransaction().setTransactionDetails(...). Each payment transaction holds at most one GiftCardTransactionDetails object. A later refund replaces prior capture values.
Field
Use
approvalCode
Successful capture or refund. Leave unset on errors.
Human-readable processor text. Language is provider-defined.
You may also set paymentTransaction.setStatus(...) (for example STATUS_CAPTURED) to match processor state.
GiftCardPaymentDetails
Instrument details expose last4 only (dw.extensions.payments.GiftCardPaymentDetails). Don’t store the full card number, PIN, or CVC on payment details, transaction details, custom attributes, or logs.
Interaction With Payment and Fraud Apps
Gift-card instruments are separate from claimed payment methods. Payment apps don’t claim COMMERCE_APP_GIFT_CARD.
Mixed tender is one gift card plus one claimed method (typically a credit card) for any remainder. Capture still runs first. When the later tender fails, gift-card refund runs as part of fail-order, after reversePayment.
When a fraud app is installed, beforePayment runs on the basket before the order exists, and afterPayment runs after gift-card capture and claimed-payment authorization are both complete. A post-authorization NOT_GUARANTEED fails the order and refunds the captured gift card. See Fraud Commerce Apps and Payment Commerce Apps.
Gift Card UI Targets
Storefront Next exposes named extension slots for the apply form, the applied amount, and My Account gift-card management. A gift card Commerce App registers React components against these targets. The hooks alone don’t render a shopper-facing card form. See Building Your Commerce App for how target-config.json maps components to target IDs.
Surface
Target ID
What the Component Does
Cart order summary
sfcc.cart.giftCards.apply
Collect brand, card number, and CVC, and then call the balance and apply Shopper APIs.
Order summary
sfcc.orderSummary.giftCards.applied
Show the masked card (last4) and the allocated amount.
My Account, payment methods
sfcc.myAccountPaymentMethods.giftCards.manage
Gift-card management on the saved-payment-methods page.
Storefront checklist
Branch on order status for every HTTP 200 from a call that can run attemptOrderTransition.
After status: failed, reuse the reopened basket. The basket ID remains valid.
Show apply errors from the HTTP 400 detail (duplicate card, negative amount, amount above balance, missing credentials, currency mismatch, zero balance).
Remove and re-apply to change the amount. PATCH rejects a shopper-provided amount and credential replacement.
Keep full gift-card numbers out of client traces and logs.
Leave native GIFT_CERTIFICATE checkout on its own payment method.
Beta Limitations
A basket supports one Commerce App gift card.
The platform allocates the gift-card amount at apply time. When basket totals change after apply, remove and re-apply the card so the allocation matches the new unpaid remainder.
Refund after fail or cancel is best effort at the platform layer. The app owns idempotent processor refunds and operations tooling for any refund the processor rejects.
Custom merchant cartridges must leave these extension points unregistered.