diff --git a/badges-flow-mvp.svg b/badges-flow-mvp.svg index 20728c8b3a..fe895e147a 100644 --- a/badges-flow-mvp.svg +++ b/badges-flow-mvp.svg @@ -1 +1 @@ -Supporter badges - every screen, in the app and on the webinteraction design - the app in its native idiom, the site in its ownplans/2026-08-21-badges-web-checkout.mdeditable SVGIn the appiOS idiom shown; Android and desktop differ only in chromeSupport SimpleXSimpleX is built by people who believeprivate messaging should not depend onadvertising. A badge helps pay for it.Why SimpleX is built this wayChoose your levelRedeem badge codeA1. Where it startsBoth actions ship today. On iOS and Play, 'Redeembadge code' is not a secondary path - it is the onlyone, because store evidence is not verified and thestore purchase was removed rather than left chargingfor nothing.‹SupportYour levelBoth levels remove the file size limit.Legend raises it further.Supporter$7 / month2 GB filesLegend$70 / month5 GB filesPerks are app constants, not catalog data -the catalog carries prices only.ContinueA2. Choose your levelPrices come from CRBadgeCatalog, never the storeproducts. One source, so the app and the site cannotdisagree about what a badge costs.‹Your levelLegendPrepaid months. Nothing renews by itself.1 month$703 months$140save 33%12 months$420save 50%3 months is 2x the monthly price, 12 is 6x. Thesaving compares against the undiscounted totaland is never sent anywhere - the server pricesevery charge it makes.Continue in browserRedeem badge codeA3. How long - desktop and Android fossThe only builds with more than one way to pay, andthe only ones that link out. 'Continue in browser'carries the answers so far as URL parameters.‹Your levelLegendPrepaid months. Nothing renews by itself.1 month$703 months$140save 33%12 months$420save 50%Buy a code at badges.simplex.chat from anybrowser, then redeem it here.Redeem badge codeno purchase button on this buildA3b. How long - iOS and PlaySame screen, different ending. The store purchaseaction is removed for the whole of this plan, and nolink out replaces it: Apple and Google rejectsteering to outside purchase for digital goods, sothe screen simply ends at redemption.‹LegendHow to payCheckout happens in your browser.Nothing is charged in the app.CardVisa, MastercardBitcoinon-chainMoneroon-chainContinue in browserOpens badges.simplex.chat with tier, monthsand method already chosen.A4. Only where there is a choiceDesktop and Android foss only. The store builds haveno methods at all and never reach this screen. Thechoice is a hint: the site asks again if theparameter is unusable.‹SupportRedeem codePaste the code from your receipt.SXB-9K2M4-7QRT1-XZ5WPasteRedeemFormats as you type and folds I and L to 1,O to 0 - the same normalisation the servicedoes, so a code read off a screen cannot failon ambiguity alone.A5. RedeemingTwenty Crockford characters in five groups. The checkcharacter is verified before anything is sent, so amistyped code never reaches the service and nevercosts a throttle token.‹SettingsSupporter perksLegendshown on your profileENDS24 August 2027Prepaid months have no billing date. The badgeis reissued each month from the balance youalready paid for, and ends when it runs out.Add more monthsA6. After redeeming'Ends', never 'renews' - nothing recurs and the copymust not imply it does. The date is the paid-throughdate from the ledger balance, not the credential'sexpiry, which is a week-aligned internal the usernever sees.‹ChatsAliceAAliceLegendlast seen recentlyIN GROUPSAAliceLegendBBobSupporterCCarolThe badge travels with the profile, so it showswherever a profile does - in chats, in a memberlist, on a contact card.A7. How everyone else sees itThe point of the whole flow. The badge is a signedcredential carried in the profile and verified by therecipient's own client against the issuer key italready ships - not a flag the server asserts, andnot something a client can set for itself.For the operatorthe CLI that mints and resolves codessimplex-badge-service$simplex-badge-service codes issue \--type legend --months 12 --count 3 \--batch conf-2026 --expires 2027-08-24SXB-9K2M4-7QRT1-XZ5WB-3NHD8SXB-2W7XP-4LMQ8-9DKR3-6TVZ5SXB-8HYNC-1FGJ6-5PBW2-7QSXD3 codes issued. Printed once; only hashes stored.OP1. Minting codesCompensation and promotional codes have no orderbehind them, so they are random rather than derived.Printed once to stdout and never again - the databaseholds only SHA-256 hashes, which is what makes astolen copy useless.support$simplex-badge-service codes status \--ref K7M2Qorder 8f3a...c21e paid 24 Augbadge legend, 12 monthscode redeemed 2 Sep--reveal refused: code already redeemedOP2. Resolving a referenceThe customer quotes the five-character reference fromtheir receipt - never the order id, never the code.--reveal is refused once a code is redeemed orrevoked, on the same reasoning that stops the webpage disclosing it.On the webbadges.simplex.chat - one centred column, max-width 560, the logo the only imagebadges.simplex.chatSupport SimpleXSimpleX has no ads, no user accounts and nothing to sell.A supporter badge helps pay for the people who build it.Supporter$7 / month - 2 GB filesLegend$70 / month - 5 GB filesChoose your levelAlready bought a code?Redeem it in the app: Settings, Supporter perks.The badge shows on your profile. Nothing renews byitself, and no account is created.simplex.chat/contactB1. The landing pageNot in the plan, and it should be. D3 specifies four screens starting at the tierquestion, which assumes everyone arrives from the app. Someone reaching the site froma link or a search needs to know what this is before being asked to choose a level -and someone who already has a code needs telling that redemption happens in the app,not here.badges.simplex.chat/#/tierChoose your levelSupport SimpleX and lift the file size limit.Supporter$7 / month2 GB filesLegend$70 / month5 GB filesContinuesimplex.chat/contactB2. Question one: the levelThe wizard proper starts here. One centred column at max-width 560, generouswhitespace, accent #0053D0 - deliberately not the app's chrome, because it is not theapp. One question per screen, and a tier with no active price renders disabled ratherthan hidden.badges.simplex.chat/#/monthsHow long?Prepaid months. Nothing renews by itself.1 month$703 months$140save 33%12 months$420save 50%Continuesimplex.chat/contactB3. How longEvery figure comes from GET /api/catalog, the same totals the RPC catalog gives theapp. The browser formats minor units and computes the comparison only - it nevermultiplies a price by a month count.badges.simplex.chat/#/payHow would you like to pay?Card is handled by Stripe. Bitcoin and Moneroare on-chain, through BTCPay.CardVisa, MastercardBitcoinon-chainMoneroon-chainContinuesimplex.chat/contactB4. MethodA method whose provider is not configured is refused at checkout withprovider_unavailable rather than hidden here, so a half-configured deployment failsloudly instead of quietly offering less. See C4.badges.simplex.chat/?tier=legend&months=12&pay=xmrCheck your orderLevelLegendDuration12 monthsMethodMoneroTotal$420.00Pay with MoneroYou will be shown an address and a QR code.simplex.chat/contactB5. Where the app hand-off landsA user arriving from the app skips B1 to B3 entirely: ?tier=legend&months=12&pay=xmranswers all three questions, so the summary is the first thing they see. POST/api/checkout then carries priceId, offerId and method - never an amount, never abadge type. Both are derived server-side from the ids, so a tampered request cannotbuy a Legend badge at a Supporter price.badges.simplex.chat/?order=8f3a...c21eSend 1.482 XMR$420.00 - this rate is held for 58:12MONERO ADDRESS48HqK2...9fRtWcCopyWaiting for the payment to confirmREFERENCEK7M2QBookmark this page - the address and the countdownboth live on this URL.simplex.chat/contactB6. Paying in cryptoPolls GET /api/order every 2s for the first minute then every 10s, and stops while thetab is hidden. A partial payment stays 'pending' - partials are the normal first eventof a multi-transaction payment, not a failure.badges.simplex.chat/?order=8f3a...c21e✓Paid. Here is your code.SXB-9K2M4-7QRT1-XZ5WB-3NHD8Copy codescan to carry it to your phoneREDEEM IT IN THE APPSimpleX -> Settings -> Supporter perks -> Redeem codeThis is the only place the code is shown.This page works until 23 September, and stops as soon asthe code is redeemed. The link is the code - treat it so.simplex.chat/contactB7. The code, onceDerived from the order id rather than stored, so a reload recomputes it while only itshash is ever at rest. Disclosure stops at redemption, at revocation, or 30 days aftersettlement - whichever comes first.badges.simplex.chat/?order=51c7...9d02Payment receivedWaiting for the card network to confirm.Still processingThis usually takes a few seconds. The page updates itself.If nothing happens for 15 minutes, we stop waitingand show you what to do next.simplex.chat/contactB8. Coming back from the card flowStripe's success_url returns here with ?order=. The return is NOT proof of payment -only the webhook settles an order, so this screen polls rather than celebrating. Thecancel URL deliberately carries no order reference at all.‹SupportYour levelBoth levels remove the file size limit.Supporter—price unavailableLegend—price unavailableCould not reach the badge serviceinternalContinueDisabled, not hidden - a level that exists butcannot be priced still exists.A2b. The service is unreachableEvery option renders disabled and nothing crashes.The same state covers a tier whose price was disabledby the operator - the app cannot tell the two apart,and does not need to.‹SupportRedeem codeThis code isn't validcode_invalidThis code has already been usedcode_usedThis code has expiredcode_expiredToo many attempts. Try again in 4 minutes.rate_limited - retryAfter 240Could not verify the credentialinternalOne message per error the service can return.Anything else, including a credential that failsto verify locally, shows the service's own textrather than a blank screen.A5b. Every redemption errorA revoked code is deliberately indistinguishable froman unknown one - both say 'isn't valid', so a guesserlearns nothing from a revocation. Support tells themapart with codes status; the wire does not.‹SettingsSupporter perksLegendended 24 August 2027Your badge has endedThe months you paid for have all been issued.The badge stopped showing on your profile;nothing was cancelled and nothing is owed.Add more monthsA6b. The balance runs outAn exhausted balance is not an error: the servicereturns no credential and a zero-balance statement,and the app says so plainly. Prepaid means it simplystops.badges.simplex.chat/#/payHow would you like to pay?Monero is temporarily unavailableTry another method, or come back later.CardVisa, MastercardBitcoinon-chainMonerounavailableContinuesimplex.chat/contactB4b. A provider is not configured503 provider_unavailable from /api/checkout. Themethod is shown and disabled rather than omitted, soan operator who forgot a section sees it immediatelyinstead of wondering why nobody pays in Monero.badges.simplex.chat/#/checkoutThese prices have changedStart again with the current pricesThe badge you chose was repriced while you were deciding.Nothing was charged.Start againprice_disabled / offer_disabled / offer_mismatchsimplex.chat/contactB5b. The catalog moved underneathPrices are checked at checkout and only there. Adeprecated price is still honoured for someone mid-flow; a disabled one is refused. Repricing appends anew price rather than editing the old, so this israre but reachable.badges.simplex.chat/#/checkoutToo many attemptsTry again in 46 secondsThe Pay button is disabled until then.Pay with Monero429, with Retry-After. The polling loop backs off tothat value rather than treating it as a failure.simplex.chat/contactB5c. Rate limitedFive checkout requests a minute per IP, because eachone reaches a payment provider. Order polling getssixty. The redemption path has no IP at all - it runsover SimpleX RPC and is throttled per signer instead.badges.simplex.chat/?order=8f3a...c21eThis invoice expired0.734 XMR arrived, which is not the full amountThe rate window has closed, so the shortfall is no longermeaningful. Quote the reference below and we will sort it out.REFERENCEK7M2QStart a new invoiceNever shows the code, and never the ?order= URL.simplex.chat/contactB6b. Expired, and underpaidThe one state that needs a human. An expired-but-paidinvoice still settles later - late on-chainsettlement is routine and moves the order to paid -but an underpaid one stops here with the referencesupport resolves by.badges.simplex.chat/?order=8f3a...c21eThis code has been redeemedThe code is no longer shown hereIt was redeemed on 2 September. If that was not you,get in touch.Legend, 12 monthspaid 24 AugustThe order stays; the capability does not.simplex.chat/contactB7b. The page outlives the codeDisclosure ends at redemption, at revocation, or 30days after settlement. The order is still herebecause support resolves a reference against it - butthe URL stops being equivalent to the code, which isthe whole point.Choose levelContinueThe catalog fetch fails, or theoperator disabled the price.ContinueOn iOS and Play there is nothing tolink out to, so the screen ends atredemption.Redeem badge codeRedeem badge code - the only route on a store builda valid codeAny error the service returns:code_invalid, code_used, code_expiredor rate_limited.on the profileThe last paid month has been issuedand the balance is zero.Choose levelContinueContinueContinueThe provider's section is missing from badge_service.ini.Pay with MoneroThe price was disabled while the buyer was deciding.Five checkout requests inside one minute, from one IP.it confirmsThe invoice expired with less than the full amount received.Redeemed, revoked, or thirty days after settlement.Pay by card - Stripe returns here, but only the webhook settles the orderContinue in browsertier, months and method travel as URL parameters, so the site opens at B5the code is pasted into A5the only thing that crosses from the site to the appDesign document. App screens are drawn in the iOS idiom; Android and desktop differ only in chrome. The site is drawn in its own idiom - one centred column at max-width 560, generous whitespace, accent #0053D0 from website/tailwind.config.js - and is deliberately not a copy of the app's. Badge artwork is the shipped art, taken verbatim from MR/images/badge_{supporter,legend}.svg.One deliberate deviation from the plan: D2 specifies the SimpleX logo as the site's only image, but the tier cards here carry the badge art too, on the grounds that someone choosing between two badges should see them. Everything else follows D2 as written.Built today: the service schema, the pricing catalog, the ledger, code derivation and classification, credential signing, the RPC dispatcher and the operator CLI. Design, not yet built: every screen above, the web listener and its endpoints, both payment providers, and the client redeem path.Store purchase is removed on iOS and Play for the duration of this plan: store evidence is not verified, so a store purchase would charge for a badge that is never issued. \ No newline at end of file +Supporter badges - every screen, in the app and on the webinteraction design - the app in its native idiom, the site in its ownplans/2026-08-21-badges-web-checkout.mdeditable SVGIn the appiOS idiom shown; Android and desktop differ only in chromeSupport SimpleXSimpleX is built by people who believeprivate messaging should not depend onadvertising. A badge helps pay for it.Why SimpleX is built this wayChoose your levelRedeem badge codeA1. Where it startsBoth actions ship today. On iOS and Play, 'Redeembadge code' is not a secondary path - it is the onlyone, because store evidence is not verified and thestore purchase was removed rather than left chargingfor nothing.‹SupportYour levelBoth levels remove the file size limit.Legend raises it further.Supporter$7 / month2 GB filesLegend$70 / month5 GB filesPerks are app constants, not catalog data -the catalog carries prices only.ContinueA2. Choose your levelPrices come from CRBadgeCatalog, never the storeproducts. One source, so the app and the site cannotdisagree about what a badge costs.‹Your levelLegendPrepaid months. Nothing renews by itself.1 month$703 months$140save 33%12 months$420save 50%3 months is 2x the monthly price, 12 is 6x. Thesaving compares against the undiscounted totaland is never sent anywhere - the server pricesevery charge it makes.Continue in browserRedeem badge codeA3. How long - desktop and Android fossThe only builds with more than one way to pay, andthe only ones that link out. 'Continue in browser'carries the answers so far as URL parameters.‹Your levelLegendPrepaid months. Nothing renews by itself.1 month$703 months$140save 33%12 months$420save 50%Buy a code at badges.simplex.chat from anybrowser, then redeem it here.Redeem badge codeno purchase button on this buildA3b. How long - iOS and PlaySame screen, different ending. The store purchaseaction is removed for the whole of this plan, and nolink out replaces it: Apple and Google rejectsteering to outside purchase for digital goods, sothe screen simply ends at redemption.‹LegendHow to payCheckout happens in your browser.Nothing is charged in the app.CardVisa, MastercardBitcoinon-chainMoneroon-chainContinue in browserOpens badges.simplex.chat with tier, monthsand method already chosen.A4. Only where there is a choiceDesktop and Android foss only. The store builds haveno methods at all and never reach this screen. Thechoice is a hint: the site asks again if theparameter is unusable.‹SupportRedeem codePaste the code from your receipt.SXB-9K2M4-7QRT1-XZ5WPasteRedeemFormats as you type and folds I and L to 1,O to 0 - the same normalisation the servicedoes, so a code read off a screen cannot failon ambiguity alone.A5. RedeemingTwenty Crockford characters in five groups. The checkcharacter is verified before anything is sent, so amistyped code never reaches the service and nevercosts a throttle token.‹SettingsSupporter perksLegendshown on your profileENDS24 August 2027Prepaid months have no billing date. The badgeis reissued each month from the balance youalready paid for, and ends when it runs out.Add more monthsA6. After redeeming'Ends', never 'renews' - nothing recurs and the copymust not imply it does. The date is the paid-throughdate from the ledger balance, not the credential'sexpiry, which is a week-aligned internal the usernever sees.‹ChatsAliceAAliceLegendlast seen recentlyIN GROUPSAAliceLegendBBobSupporterCCarolThe badge travels with the profile, so it showswherever a profile does - in chats, in a memberlist, on a contact card.A7. How everyone else sees itThe point of the whole flow. The badge is a signedcredential carried in the profile and verified by therecipient's own client against the issuer key italready ships - not a flag the server asserts, andnot something a client can set for itself.For the operatorthe CLI that mints and resolves codessimplex-badge-service$simplex-badge-service codes issue \--type legend --months 12 --count 3 \--batch conf-2026 --expires 2027-08-24SXB-9K2M4-7QRT1-XZ5WB-3NHD8SXB-2W7XP-4LMQ8-9DKR3-6TVZ5SXB-8HYNC-1FGJ6-5PBW2-7QSXD3 codes issued. Printed once; only hashes stored.OP1. Minting codesCompensation and promotional codes have no orderbehind them, so they are random rather than derived.Printed once to stdout and never again - the databaseholds only SHA-256 hashes, which is what makes astolen copy useless.support$simplex-badge-service codes status \--ref K7M2Qorder 8f3a...c21e paid 24 Augbadge legend, 12 monthscode redeemed 2 Sep--reveal refused: code already redeemedOP2. Resolving a referenceThe customer quotes the five-character reference fromtheir receipt - never the order id, never the code.--reveal is refused once a code is redeemed orrevoked, on the same reasoning that stops the webpage disclosing it.On the webbadges.simplex.chat - one centred column, max-width 560, the logo the only imagebadges.simplex.chatSupport SimpleXSimpleX has no ads, no user accounts and nothing to sell.A supporter badge helps pay for the people who build it.Supporter$7 / month - 2 GB filesLegend$70 / month - 5 GB filesChoose your levelAlready bought a code?Redeem it in the app: Settings, Supporter perks.The badge shows on your profile. Nothing renews byitself, and no account is created.simplex.chat/contactB1. The landing pageNot in the plan, and it should be. D3 specifies four screens starting at the tierquestion, which assumes everyone arrives from the app. Someone reaching the site froma link or a search needs to know what this is before being asked to choose a level -and someone who already has a code needs telling that redemption happens in the app,not here.badges.simplex.chat/#/tierChoose your levelSupport SimpleX and lift the file size limit.Supporter$7 / month2 GB filesLegend$70 / month5 GB filesContinuesimplex.chat/contactB2. Question one: the levelThe wizard proper starts here. One centred column at max-width 560, generouswhitespace, accent #0053D0 - deliberately not the app's chrome, because it is not theapp. One question per screen, and a tier with no active price renders disabled ratherthan hidden.badges.simplex.chat/#/monthsHow long?Prepaid months. Nothing renews by itself.1 month$703 months$140save 33%12 months$420save 50%Continuesimplex.chat/contactB3. How longEvery figure comes from GET /api/catalog, the same totals the RPC catalog gives theapp. The browser formats minor units and computes the comparison only - it nevermultiplies a price by a month count.badges.simplex.chat/#/payHow would you like to pay?Card is handled by Stripe. Bitcoin and Moneroare on-chain, through BTCPay.CardVisa, MastercardBitcoinon-chainMoneroon-chainContinuesimplex.chat/contactB4. MethodA method whose provider is not configured is refused at checkout withprovider_unavailable rather than hidden here, so a half-configured deployment failsloudly instead of quietly offering less. See C4.badges.simplex.chat/?tier=legend&months=12&pay=xmrCheck your orderLevelLegendDuration12 monthsMethodMoneroTotal$420.00Pay with MoneroYou will be shown an address and a QR code.simplex.chat/contactB5. Where the app hand-off landsA user arriving from the app skips B1 to B3 entirely: ?tier=legend&months=12&pay=xmranswers all three questions, so the summary is the first thing they see. POST/api/checkout then carries priceId, offerId and method - never an amount, never abadge type. Both are derived server-side from the ids, so a tampered request cannotbuy a Legend badge at a Supporter price.badges.simplex.chat/?order=8f3a...c21eSend 1.482 XMR$420.00 - this rate is held for 58:12MONERO ADDRESS48HqK2...9fRtWcCopyWaiting for the payment to confirmREFERENCEK7M2QBookmark this page - the address and the countdownboth live on this URL.simplex.chat/contactB6. Paying in cryptoPolls GET /api/order every 2s for the first minute then every 10s, and stops while thetab is hidden. A partial payment stays 'pending' - partials are the normal first eventof a multi-transaction payment, not a failure.badges.simplex.chat/?order=8f3a...c21e✓Paid. Here is your code.SXB-9K2M4-7QRT1-XZ5WB-3NHD8Copy codescan to carry it to your phoneREDEEM IT IN THE APPSimpleX -> Settings -> Supporter perks -> Redeem codeThis is the only place the code is shown.This page works until 23 September, and stops as soon asthe code is redeemed. The link is the code - treat it so.simplex.chat/contactB7. The code, onceDerived from the order id rather than stored, so a reload recomputes it while only itshash is ever at rest. Disclosure stops at redemption, at revocation, or 30 days aftersettlement - whichever comes first.badges.simplex.chat/?order=51c7...9d02Payment receivedWaiting for the card network to confirm.Still processingThis usually takes a few seconds. The page updates itself.If nothing happens for 15 minutes, we stop waitingand show you what to do next.simplex.chat/contactB8. Coming back from the card flowStripe's success_url returns here with ?order=. The return is NOT proof of payment -only the webhook settles an order, so this screen polls rather than celebrating. Thecancel URL deliberately carries no order reference at all.‹SupportYour levelBoth levels remove the file size limit.Supporter—price unavailableLegend—price unavailableCould not reach the badge serviceinternalContinueDisabled, not hidden - a level that exists butcannot be priced still exists.A2b. The service is unreachableEvery option renders disabled and nothing crashes.The same state covers a tier whose price was disabledby the operator - the app cannot tell the two apart,and does not need to.‹SupportRedeem codeThis code isn't validcode_invalidThis code has already been usedcode_usedThis code has expiredcode_expiredToo many attempts. Try again in 4 minutes.rate_limited - retryAfter 240Could not verify the credentialinternalOne message per error the service can return.Anything else, including a credential that failsto verify locally, shows the service's own textrather than a blank screen.A5b. Every redemption errorA revoked code is deliberately indistinguishable froman unknown one - both say 'isn't valid', so a guesserlearns nothing from a revocation. Support tells themapart with codes status; the wire does not.‹SettingsSupporter perksLegendended 24 August 2027Your badge has endedThe months you paid for have all been issued.The badge stopped showing on your profile;nothing was cancelled and nothing is owed.Add more monthsA6b. The balance runs outAn exhausted balance is not an error: the servicereturns no credential and a zero-balance statement,and the app says so plainly. Prepaid means it simplystops.badges.simplex.chat/#/payHow would you like to pay?Monero is temporarily unavailableTry another method, or come back later.CardVisa, MastercardBitcoinon-chainMonerounavailableContinuesimplex.chat/contactB4b. A provider is not configured503 provider_unavailable from /api/checkout. Themethod is shown and disabled rather than omitted, soan operator who forgot a section sees it immediatelyinstead of wondering why nobody pays in Monero.badges.simplex.chat/#/checkoutThese prices have changedStart again with the current pricesThe badge you chose was repriced while you were deciding.Nothing was charged.Start againprice_disabled / offer_disabled / offer_mismatchsimplex.chat/contactB5b. The catalog moved underneathPrices are checked at checkout and only there. Adeprecated price is still honoured for someone mid-flow; a disabled one is refused. Repricing appends anew price rather than editing the old, so this israre but reachable.badges.simplex.chat/#/checkoutToo many attemptsTry again in 46 secondsThe Pay button is disabled until then.Pay with Monero429, with Retry-After. The polling loop backs off tothat value rather than treating it as a failure.simplex.chat/contactB5c. Rate limitedFive checkout requests a minute per IP, because eachone reaches a payment provider. Order polling getssixty. The redemption path has no IP at all - it runsover SimpleX RPC and is throttled per signer instead.badges.simplex.chat/?order=8f3a...c21eThis invoice expired0.734 XMR arrived, which is not the full amountThe rate window has closed, so the shortfall is no longermeaningful. Quote the reference below and we will sort it out.REFERENCEK7M2QStart a new invoiceNever shows the code, and never the ?order= URL.simplex.chat/contactB6b. Expired, and underpaidThe one state that needs a human. An expired-but-paidinvoice still settles later - late on-chainsettlement is routine and moves the order to paid -but an underpaid one stops here with the referencesupport resolves by.badges.simplex.chat/?order=8f3a...c21eThis code has been redeemedThe code is no longer shown hereIt was redeemed on 2 September. If that was not you,get in touch.Legend, 12 monthspaid 24 AugustThe order stays; the capability does not.simplex.chat/contactB7b. The page outlives the codeDisclosure ends at redemption, at revocation, or 30days after settlement. The order is still herebecause support resolves a reference against it - butthe URL stops being equivalent to the code, which isthe whole point.Choose levelContinueThe catalog fetch fails, or theoperator disabled the price.ContinueOn iOS and Play there is nothing tolink out to, so the screen ends atredemption.Redeem badge code - from either duration screen, and the only route on a store builda valid codeAny error the service returns:code_invalid, code_used, code_expiredor rate_limited.on the profileThe last paid month has been issuedand the balance is zero.Choose levelContinueContinueContinueThe provider's section is missing from badge_service.ini.Pay with MoneroThe price was disabled while the buyer was deciding.Five checkout requests inside one minute, from one IP.it confirmsThe invoice expired with less than the full amount received.Redeemed, revoked, or thirty days after settlement.Pay by card - Stripe returns here, but only the webhook settles the orderContinue in browsertier, months and method travel as URL parameters, so the site opens at B5the code is pasted into A5the only thing that crosses from the site to the appDesign document. App screens are drawn in the iOS idiom; Android and desktop differ only in chrome. The site is drawn in its own idiom - one centred column at max-width 560, generous whitespace, accent #0053D0 from website/tailwind.config.js - and is deliberately not a copy of the app's. Badge artwork is the shipped art, taken verbatim from MR/images/badge_{supporter,legend}.svg.One deliberate deviation from the plan: D2 specifies the SimpleX logo as the site's only image, but the tier cards here carry the badge art too, on the grounds that someone choosing between two badges should see them. Everything else follows D2 as written.Built today: the service schema, the pricing catalog, the ledger, code derivation and classification, credential signing, the RPC dispatcher and the operator CLI. Design, not yet built: every screen above, the web listener and its endpoints, both payment providers, and the client redeem path.Store purchase is removed on iOS and Play for the duration of this plan: store evidence is not verified, so a store purchase would charge for a badge that is never issued. \ No newline at end of file