View Issue Details

IDProjectCategoryView StatusLast Update
0007841Talerspecificationpublic2026-01-25 16:26
Reportersebasjm Assigned ToResolution deadline
ImplementerReviewer 
Release signed byDeployed by 
VerifierCommunicator 
Data protection authority notification requiredNo  
CSIRT notification requiredNo  
Estimated effort (hours)Actual effort (hours)0 
Fast-trackNo
Work packagenone
PrioritynormalSeverityfeatureReproducibilityN/A
Status acknowledgedResolutionopen 
Product Versiongit (master) 
Target Versionpost-2.0 
Summary0007841: merchant should require wallet to accept or reject refund
Descriptionin current protocol refunds are unilateral decisions, when the merchant create a refund this is assume that is good for the wallet since is more money for the pocket.
the merchant is assume to behave in a good way but there are situations that this can be exploited

consider the scenario of selling ticket of concerts:

over sell
---
1.- merchant has 100 seats to sells
2.- merchant sells 110 seats
3.- choose 10 seats based on some factors to refund

speculative price
--
1.- merchant has 100 seats to sells
2.- merchant sells 100 seats at price X
3.- sold in 2 days, still 2 month for the performance
4.- refund 20%, double the price and sell again

in all cases
1.- wallet will automatically pickup refunds
2.- wallet user can't complain since refund was accepted and money is in their pockets

more importantly:

A. if the refund does cancel the contract: wallet doesn't have a warrant that after the purchase the merchant is not going to unilateral cancel the contract
B. if the refund doesn't cancel the contract: merchant can't have a way to deny the entrance (or the service in the contact terms) after a refund.

we want to make this work without reducing the refundable duration since in the example of a ticket for a concert, the refundable duration should be between the purchase and the performance.

proposal:
---

https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-orders-$ORDER_ID-refund

interface WalletRefundRequest {
  // Hash of the order's contract terms (this is used to authenticate the
  // wallet/customer).
  h_contract: HashCode;
 
  // accepted -> actual case
  // denied -> return the amount to the merchant, as nothing happened
  decision: "accepted" | "denied"
}
TagsNo tags attached.

Required notifications

Address or linkRemarkAdded byNotifiedActions

Activities

Christian Grothoff

2023-05-13 18:36

manager   ~0020195

I think your report here is filed under a false legal premise. By merely (especially automatically) accepting a refund, a customer does not inherently forfeit their rights to the contract. So the customer could still go after the merchant for not fulfilling their original agreement. And in your scenario where the merchant behaved in bad faith, I'm pretty sure they'd loose in court. On the other hand, if the merchant could not be expected to uphold their end (say concert was cancelled due to sickness or natural disasters), then the merchant would be in their rights to unilaterally break the contract like this. But, again, this is something for the courts. So before implementing anything here, we definitively need to talk to an actual contract lawyer....

Issue History

Date ModifiedUsernameFieldChange
2023-05-10 15:53sebasjmNew Issue
2023-05-10 15:53sebasjmStatusnew => assigned
2023-05-10 15:53sebasjmAssigned To => Christian Grothoff
2023-05-10 15:54Florian DoldTarget Version => post-1.0
2023-05-10 15:54Florian DoldSummarymerchant required wallet to accept or reject refund => merchant should require wallet to accept or reject refund
2023-05-13 18:33Christian GrothoffSeveritymajor => feature
2023-05-13 18:33Christian GrothoffReproducibilityalways => N/A
2023-05-13 18:36Christian GrothoffNote Added: 0020195
2023-05-13 18:36Christian GrothoffStatusassigned => acknowledged
2023-05-13 20:18Christian GrothoffAssigned ToChristian Grothoff =>
2024-01-12 13:02Christian GrothoffCategorymerchant backend API (HTTP specification) => specification
2026-01-25 16:26Christian GrothoffTarget Versionpost-1.0 => post-2.0