Skip to content

Release v3.4.0: coupon payment references and split coupon recording - #43

Merged
ManulParihar merged 14 commits into
mainfrom
feature/coupon
Oct 4, 2026
Merged

ManulParihar merged 14 commits into
mainfrom
feature/coupon

Conversation

@ManulParihar

Copy link
Copy Markdown
Member

Coupon payments: tagged payment references and split recording

What this does

Lets the backend record coupon purchases onchain without any contract change. A coupon purchase is recorded as Fiat in USD, and its payment reference carries a tag in the high bytes that the order id leaves free. A coupon that covers only part of the price is recorded as two lines of the same order, and the SDK can now record both in one call.

Payment reference tags

The backend's reference is the order's Mongo ObjectId, left-padded to 32 bytes, so the high 20 bytes are always zero. The SDK writes a tag there:

Kind Tag Used for
Standard none card, external wallet or device wallet, no coupon
Coupon 0xfee0ff order paid in full by a coupon
CouponPart 0xfee0ffc0de the coupon's share of a split order
Remainder 0xfee0ffba1a5ce0 what the user paid on top of the coupon
  • Every coupon tag starts with 0xfee0ff, so coupon purchases are visible on a block explorer.
  • Both lines of a split keep the same order id, so either one leads back to the order.
  • Existing references have no tag and read back as Standard, so nothing already onchain changes.

New on KokioAdmin.utils:

  • tagPaymentReference(reference, kind): writes the tag. Refuses a reference that is already tagged, not 32 bytes, or has an empty order part.
  • parsePaymentReference(reference): returns { kind, reference }, the untagged reference included. Refuses a tag the SDK did not write.

PaymentReferenceKind is exported from kokio-sdk/admin.

Recording a split coupon order

admin.registry.recordSettledPurchase keeps its current form and gains a second one:

recordSettledPurchase(eSIMWallet, details, asset, tokenAmount, [couponRef, remainderRef], couponUSDCents)

details carries the full price and how the user paid the rest. asset and tokenAmount are what they paid. The SDK then:

  1. Checks that the references are one order's CouponPart and Remainder, and that the coupon covers more than 0 and less than the full price.
  2. Records the remainder line, waits for it, then records the coupon line (Fiat, USD, the coupon's cents as tokenAmount).
  3. Skips any line already recorded, so a retry after a partial failure sends only what is missing.
  4. Returns the last transaction hash.

If both lines are already recorded, the contract refuses the call with PaymentReferenceAlreadyUsed, the same error a retried single reference gets.

This form is for coupon + card and coupon + external wallet. For coupon + device wallet the user's own purchase records the remainder line, and the backend records the coupon line with a single reference.

New errors

Both are exported from kokio-sdk/admin:

  • InvalidPaymentReferenceError: a reference that cannot be tagged or read back, or split references that do not belong together.
  • CouponSplitOutOfRangeError: a coupon that covers none or all of the price.

Tests

  • Unit tests for tagging and parsing, and for the split form: order of the lines, a spent line skipped, both spent, a reverted first line, and the input checks.
  • New consumer suite couponPayment (fork and live) that records a bundle every way it can be paid: card, full coupon, and a coupon split with card, external wallet or device wallet, plus a retried split. Each reference is read back to its order. Run with npm run test:consumer:fork.
  • The package export test covers the new enum and errors.

Compatibility

No breaking changes. Every existing call works as before, and the single-reference recordSettledPurchase does not check tags.

Version: 3.3.0 → 3.4.0

Coupon tags go in the high 20 bytes the order id leaves free, so a split
purchase gets two references that still point at the same order.
Covers card, full coupon, and a coupon split with card, an external
wallet or the device wallet, reading each reference back to its order.
Given the coupon and remainder references and the coupon's cents, the SDK
records the remainder line, then the coupon line, skipping any line already
spent so a retry after a partial failure is safe.
Coupon lines now record their cents as the token amount, and a new case
retries a split whose first line already landed.
@ManulParihar
ManulParihar merged commit 908c3a6 into main Oct 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant