Flow · 13 steps

Store credit and gift cards

Balances customers hold, as tenders: give a customer store credit and take it back, issue a gift card and look its code up, pay an order with the card, refund part of it back onto the card the money came from, and stop the card.

A gift card's code is answered once, by the issue (and its retry, the same code): no read shows it again, only its last four. Ids written {giftCardId} are read from an earlier answer.

Retrying each step. The credit, the card, the payment and the refund carry your key (creditId, giftCardId, paymentId, refundId) and answer with what the first call recorded; a reversal and a disable are idempotent by state.

Services involved

A filled dot is a read; a ring is a call that changes something.

  • Fulfillment Manager: https://fulfillment.getmanifold.io
  • Inventory Manager: https://inventory.getmanifold.io

Every call carries Authorization: Bearer $MANIFOLD_TOKEN unless its step says otherwise (getting a token). Run a flow in your sandbox, not your live organization: see Before you start.

1 Give a customer store credit

Twenty-five dollars for a late delivery, with the reason for the record. Credit is per currency.

POST /credit Fulfillment Manager

Request · application/json
{
  "creditId": "crd_01a15912-4c01-7f2a-8b3c-4d5e6f7a8b9c",
  "holderId": "cmp_01a15912-2b01-7c3d-8e4f-5a6b7c8d9e0f",
  "currency": "AUD",
  "amountCents": 2500,
  "reason": "Late delivery"
}

Answers 201. What the answer holds: the operation's example.

Records fulfillment.credit-issued.

2 Read the customer's credit

One account per currency, with its history newest first.

GET /credit?companyId=cmp_01a15912-2b01-7c3d-8e4f-5a6b7c8d9e0f Fulfillment Manager

What the answer holds: the operation's example.

3 Take it back

Issued to the wrong customer: a manual issue reverses once, while the customer still holds it.

POST /credit/crd_01a15912-4c01-7f2a-8b3c-4d5e6f7a8b9c/reverse Fulfillment Manager

Request · application/json
{
  "reason": "Issued to the wrong account"
}

What the answer holds: the operation's example.

Records fulfillment.credit-reversed.

4 Issue a gift card

Fifty dollars. The answer carries the code — give it to the customer now; no later read shows it.

POST /gift-cards Fulfillment Manager

Request · application/json
{
  "giftCardId": "gcd_01a15912-6e02-7b3c-9d4e-5f6a7b8c9d0e",
  "amountCents": 5000,
  "currency": "AUD"
}

Answers 201. What the answer holds: the operation's example.

From your answer, later steps send code as {code}, card.id as {giftCardId} and card.last4 as {last4}.

Records fulfillment.gift-card-issued.

5 Check the code

What the code is good for, before it pays. A code that matches no card is 404 and counts against you: ten in fifteen minutes and every code is refused for fifteen.

POST /gift-cards/check Fulfillment Manager

Request · application/json
{
  "code": "{code}"
}

What the answer holds: the operation's example.

Records nothing.

6 Find the card by its last four

How staff find a card a customer brings in: never by its whole code.

GET /gift-cards?last4=%7Blast4%7D Fulfillment Manager

What the answer holds: the operation's example.

7 Have something to sell

Ten mugs on hand, so the order below has stock to hold and ship.

POST /items Inventory Manager

Request · application/json
{
  "sku": "GIFT-WRAP-MUG",
  "name": "Gift-wrapped mug",
  "quantity": 10
}

Answers 201. What the answer holds: the operation's example.

Records inventory.item-created.

8 Take an order

Two mugs at eighteen dollars each, for the gift card to pay towards.

POST /orders Fulfillment Manager

Request · application/json
{
  "reference": "GIFT-1001",
  "lines": [
    {
      "sku": "GIFT-WRAP-MUG",
      "quantity": 2,
      "unitPriceCents": 1800
    }
  ],
  "customer": {
    "personId": "psn_01a15890-1b2c-7d3e-8f4a-5b6c7d8e9f0a"
  }
}

Answers 201. What the answer holds: the operation's example.

From your answer, later steps send id as {orderId}.

Records fulfillment.order-created, fulfillment.stock-allocated, fulfillment.shipment-created.

9 Pay with the gift card

The card's balance falls in the same commit as the order's, under the organization's lock: two payments racing for one card never overdraw it. A card disabled, expired, in another currency or short is refused.

POST /orders/{orderId}/payments Fulfillment Manager

Request · application/json
{
  "paymentId": "pay_01a15912-8a03-7d4e-8f5a-6b7c8d9e0f1a",
  "amountCents": 1800,
  "method": "gift-card",
  "giftCardCode": "{code}"
}

Answers 201. What the answer holds: the operation's example.

Records fulfillment.payment-recorded.

10 Refund part of it, back onto the card

The money goes back the way it came: the card that paid gets it first. The refund's tenders say so.

POST /orders/{orderId}/refunds Fulfillment Manager

Request · application/json
{
  "refundId": "rfd_01a15912-9b04-7e5f-9a6b-7c8d9e0f1a2b",
  "amountCents": 600,
  "reason": "Wrap torn",
  "lines": []
}

Answers 201. What the answer holds: the operation's example.

Records fulfillment.refunded.

11 Stop the card

Reported lost: no payment may draw on it until it is enabled again.

POST /gift-cards/{giftCardId}/disable Fulfillment Manager

Request · application/json
{
  "reason": "Reported lost"
}

What the answer holds: the operation's example.

Records fulfillment.gift-card-disabled.

12 Let it be used again

Found after all: enabled, with its balance as it stood.

POST /gift-cards/{giftCardId}/enable Fulfillment Manager

Request · application/json
{}

What the answer holds: the operation's example.

Records fulfillment.gift-card-enabled.

13 Read the card

Its balance — fifty less the payment plus the refund — and its whole history, never its code.

GET /gift-cards/{giftCardId} Fulfillment Manager

What the answer holds: the operation's example.

What a recorded fact is: Facts and the journal. Why a repeated step changes nothing: Idempotency and retries.