OpenPoly logo
Polyflight

Native wallet contract

Polyflight debit, payout, refund, and compensation requests on the existing REST v1 adapter.

Native wallet contract

Polyflight uses the existing REST v1 wallet paths and response contract. Do not create separate credentials or Polyflight-only endpoints.

Request matrix

EffectMethod and pathreasonStable idempotency_key
Ticket purchasePOST /debitmini_app_polyflight_purchasemini_app_polyflight_purchase:{purchaseRequestId}:debit
Debit applied but no ticket admittedPOST /debit-reversalmini_app_polyflight_purchase_compensationmini_app_polyflight_purchase:{purchaseRequestId}:debit_reversal
Winning payoutPOST /creditmini_app_polyflight_winmini_app_polyflight_ticket:{ticketId}:win
Cancelled-ticket refundPOST /creditmini_app_polyflight_refundmini_app_polyflight_ticket:{ticketId}:refund

The endpoint determines direction; there is no direction field in the REST v1 mutation body. A losing ticket creates no zero-value credit request.

Request shape

{
  "external_user_id": "operator-user-123",
  "amount_minor": 10000,
  "currency_code": "IDR",
  "idempotency_key": "mini_app_polyflight_purchase:6f9f6d61-3ac7-47d2-aa42-6ab39e492931:debit",
  "reason": "mini_app_polyflight_purchase",
  "metadata": {
    "product": "polyflight",
    "operation": "purchase",
    "walletTransport": "rest_v1",
    "purchaseRequestId": "6f9f6d61-3ac7-47d2-aa42-6ab39e492931",
    "flightId": "1df82a79-f8e2-4729-bced-07ca6387524f",
    "flightCode": "PF-EXAMPLE",
    "choiceKind": "fixed",
    "choiceIndex": 2,
    "currencyCode": "IDR",
    "stakeMinor": 10000,
    "feeMinor": 500,
    "netStakeMinor": 9500,
    "potentialPayoutMinor": 38000,
    "opensAt": "2026-09-16T10:00:00.000Z",
    "locksAt": "2026-09-16T10:50:00.000Z",
    "landsAt": "2026-09-16T11:00:00.000Z",
    "economyVersion": "polyflight-economy-v1"
  }
}

Every Polyflight request carries the common native-ticket context below. Treat metadata fields as additive; do not reject an otherwise valid request because a later version adds a field.

Metadata fieldMeaning
productAlways polyflight.
operationpurchase, compensation, win, or refund.
walletTransportImmutable simulator or rest_v1 transport pinned under the live purchase claim before the debit is sent.
purchaseRequestIdStable purchase intent UUID.
ticketIdTicket UUID; present for admitted-ticket effects and absent when compensation occurs before admission.
flightId, flightCodeFlight identifiers.
choiceKind, choiceIndexSelected option group and index.
currencyCodeIDR.
stakeMinor, feeMinor, netStakeMinor, potentialPayoutMinorImmutable ticket quote amounts.
opensAt, locksAt, landsAtISO-8601 flight times.
economyVersionEconomy version committed to the ticket.

Refund and compensation metadata both include originalDebitOperationId and originalDebitIdempotencyKey; win metadata does not. Polyflight metadata does not contain fake marketId or orderId values, an operator Credits balance, or a purchase fxRateSnapshotId.

The admitted ticket and every later win, refund, or compensation inherit the purchase's pinned walletTransport. Lease renewal or recovery cannot overwrite it with a different transport. This prevents an existing real-wallet obligation from being redirected through the simulator, or the reverse.

Amount by effect

  • Purchase debit: exact stakeMinor.
  • Win credit: exact stored potentialPayoutMinor.
  • Cancelled-ticket refund credit: exact original stakeMinor, including the ticket fee.
  • Unadmitted-purchase debit reversal: exact original purchase debit.

No request recalculates an amount from a current FX rate or current commercial terms.

Existing response contract

The response is unchanged from the existing debit/credit/reversal adapter:

{
  "external_user_id": "operator-user-123",
  "status": "succeeded",
  "currency_code": "IDR",
  "amount_minor": 10000,
  "idempotency_key": "mini_app_polyflight_purchase:6f9f6d61-3ac7-47d2-aa42-6ab39e492931:debit",
  "remote_reference": "wallet-op-123",
  "balance_minor": 90000
}

status is required and must be succeeded, failed, or pending. remote_reference, balance_minor, echoed identity/key fields, and failure_reason are optional under the existing adapter contract. OpenPoly preserves the immutable request amount and currency; response echoes do not replace them.

X-Polynion-Request-Id identifies one HTTP attempt for tracing. It is not the financial idempotency key.

Idempotency and ambiguous outcomes

The operator wallet must enforce all of these rules:

  • The same idempotency key and same parameters return the same effective result without applying money twice.
  • Reusing a key with different user, amount, currency, reason, or material metadata is a conflict.
  • Timeout, network failure, HTTP 408, 429, or 5xx, pending, and an invalid response are ambiguous outcomes.
  • OpenPoly does not blindly resend an ambiguous primary debit or credit.
  • An ambiguous purchase is not admitted as a ticket until authoritative evidence proves the debit applied.
  • A known applied debit that can no longer be admitted is returned through the dedicated debit-reversal key.
  • A known non-applied debit can be resolved without inventing a compensating money movement.

Operators must retain ledger evidence keyed by idempotency_key. remote_reference is useful when returned but remains optional. Manual resolution requires authenticated operator-ledger evidence even when no remote reference was returned.

Durable lifecycle values

The purchase wallet lifecycle is separate from purchase/admission status:

plannedsentsucceeded or failed / unknown / manual_review; an applied debit that cannot produce a ticket moves through compensation_pending to compensated.

Winning/refund/compensation obligations use the separate payment states owed, in_flight, paid, or manual_review. Do not infer ticket outcome from payment state.

Purchase, result, and cancellation boundaries

A successful remote debit and an admitted ticket are two separate durable steps. If debit succeeds after the flight gate closes, OpenPoly schedules exact compensation rather than forcing a late ticket. If the ticket was admitted and the flight is later cancelled, OpenPoly uses /credit with the refund key instead of /debit-reversal.

A resolved winning ticket can show payout pending or manual review while the public result remains final. External wallet delay does not change the winner. A cancelled ticket can show refund owed before the wallet credit completes. Operator or user suspension blocks new admission but does not block money already owed.

Copyright © 2026