Skip to content

fix(cash-link): report a rejected claim intent as a failure - #1570

Merged
bmc08gt merged 1 commit into
code/cashfrom
fix/cash-link-claim-intent-result
Sep 25, 2026
Merged

bmc08gt merged 1 commit into
code/cashfrom
fix/cash-link-claim-intent-result

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Sep 25, 2026

Copy link
Copy Markdown
Collaborator

ReceiveGiftCardTransactor.start() treated a rejected claim intent as a successful claim. It called .map on the account-creation Result and ran receiveRemotely() inside it. receiveRemotely() returns its own Result, so the value became Result<Result<IntentType>>. The fold after that only failed when account creation failed, so a failed intent went to onSuccess. This came in with 6d502a9.

When two people tap the same cash link, the server accepts one claim and rejects the other with SubmitIntentError.StaleState (gift card already claimed). On Android the rejected claim still went down the success path:

  • CashLinkDelegate added a phantom local balance through tokenCoordinator.add and showed a "received" bill.
  • It emitted SettledClaim(collected = true), and ChatViewModel.thankForClaim posted a duplicate "thanks for the cash" reply.
  • Analytics logged receiveCashLink as a success.

Any other intent failure after the account step, such as a denial or a network error, took the same path. iOS (Session.receiveCashLink) already handles the already-claimed case.

Changes

  • Fold the account result into receiveRemotely() so the intent's Result is the one that reaches onFailure. Error handling stays the same: the already-claimed stale state is returned without logAndFail, and every other error goes through it.
  • Add ReceiveGiftCardTransactorTest cases where receiveRemotely() returns an already-claimed StaleState and a Denied error. Both fail before the fix.
  • Remove the test file's note that MockK can't stub Result.success from getAccounts. The new tests do it on MockK 1.14.11.

ReceiveGiftCardTransactor.start() mapped the account-creation Result into
receiveRemotely(), which returns its own Result. That produced a
Result<Result<IntentType>>, and the outer fold only saw the account step.
A rejected intent landed in onSuccess and start() returned success.

When two people claim the same cash link, the server rejects the second
with SubmitIntentError.StaleState (already claimed). Android still called
onReceived for that claim: CashLinkDelegate added a phantom local balance,
showed a received bill, and emitted SettledClaim(collected = true), which
posted a duplicate "thanks for the cash" reply. Analytics logged
receiveCashLink as a success. Any other intent failure after the account
step took the same path.

Fold the account result into receiveRemotely() so the intent's Result is
the one folded. The already-claimed stale state is still returned without
logAndFail; other failures still go through it.

This regressed in 6d502a9.
@bmc08gt bmc08gt self-assigned this Sep 25, 2026
@github-actions github-actions Bot added area: network gRPC, connectivity, API, exchange rates type: fix Bug fix labels Sep 25, 2026
@bmc08gt
bmc08gt merged commit 2acfecf into code/cash Sep 25, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: network gRPC, connectivity, API, exchange rates type: fix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant