summaryrefslogtreecommitdiff
path: root/core/api-merchant.rst
blob: 43eed0369774330c96f48e2b6107cb2234f7cc80 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
..
  This file is part of GNU TALER.
  Copyright (C) 2014-2020 Taler Systems SA

  TALER is free software; you can redistribute it and/or modify it under the
  terms of the GNU General Public License as published by the Free Software
  Foundation; either version 2.1, or (at your option) any later version.

  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License along with
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>

  @author Marcello Stanisci
  @author Florian Dold
  @author Christian Grothoff

.. _merchant-api:

====================
Merchant Backend API
====================

WARNING: This document describes the version 1 of the merchant backend
API, which is NOT yet implemented at all!

The ``*/public/*`` endpoints are publicly exposed on the Internet and accessed
both by the user's browser and their wallet.

Most endpoints given here can be prefixed by a base URL that includes the
specific instance selected (BASE_URL/instances/$INSTANCE/).  If
``/instances/`` is missing, the default instance is to be used.

.. contents:: Table of Contents


-------------------------
Getting the configuration
-------------------------

.. http:get:: /public/config

  Return the protocol version and currency supported by this merchant backend.

  **Response:**

  :status 200 OK:
    The exchange accepted all of the coins. The body is a `VersionResponse`.

  .. ts:def:: VersionResponse

    interface VersionResponse {
      // libtool-style representation of the Merchant protocol version, see
      // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
      // The format is "current:revision:age".
      version: string;

      // Currency supported by this backend.
      currency: string;

      // optional array with information about the instances running at this backend
      // FIXME: remove, use/provide http:get:: /instances instead!
      instances: InstanceInformation[];
    }

  .. ts:def:: InstanceInformation

    interface InstanceInformation {

      // Human-readable legal business name served by this instance
      name: string;

      // Base URL of the instance. Can be of the form "/PizzaShop/" or
      // a fully qualified URL (i.e. "https://backend.example.com/PizzaShop/").
      instance_baseurl: string;

      // Public key of the merchant/instance, in Crockford Base32 encoding.
      merchant_pub: EddsaPublicKey;

      // List of the payment targets supported by this instance. Clients can
      // specify the desired payment target in /order requests.  Note that
      // front-ends do not have to support wallets selecting payment targets.
      payment_targets: string[];

      // Base URL of the exchange this instance uses for tipping.
      // Optional, only present if the instance supports tipping.
      // FIXME: obsolete with current tipping API!
      tipping_exchange_baseurl?: string;

    }


------------------
Receiving Payments
------------------

.. _post-order:

.. http:post:: /create-order

  Create a new order that a customer can pay for.

  This request is **not** idempotent unless an ``order_id`` is explicitly specified.

  .. note::

    This endpoint does not return a URL to redirect your user to confirm the
    payment.  In order to get this URL use :http:get:`/orders/$ORDER_ID`.  The
    API is structured this way since the payment redirect URL is not unique
    for every order, there might be varying parameters such as the session id.

  **Request:**

  The request must be a `PostOrderRequest`.

  :query payment_target: optional query that specifies the payment target preferred by the client. Can be used to select among the various (active) wire methods supported by the instance.

  **Response**

  :status 200 OK:
    The backend has successfully created the proposal.  The response is a
    :ts:type:`PostOrderResponse`.

  .. ts:def:: PostOrderRequest

    interface PostOrderRequest {
      // The order must at least contain the minimal
      // order detail, but can override all
      order: MinimalOrderDetail | ContractTerms;
    }

  The following fields must be specified in the ``order`` field of the request.  Other fields from
  `ContractTerms` are optional, and will override the defaults in the merchant configuration.

  .. ts:def:: MinimalOrderDetail

    interface MinimalOrderDetail {
      // Amount to be paid by the customer
      amount: Amount

      // Short summary of the order
      summary: string;

      // URL that will show that the order was successful after
      // it has been paid for.  The wallet must always automatically append
      // the order_id as a query parameter to this URL when using it.
      fulfillment_url: string;
    }

  .. ts:def:: PostOrderResponse

    interface PostOrderResponse {
      // Order ID of the response that was just created
      order_id: string;
    }



.. http:get:: /orders

  Returns known orders up to some point in the past

  **Request**

  :query paid: *Optional*. If set to yes, only return paid orders, if no only unpaid orders. Do not give (or use "all") to see all orders regardless of payment status.
  :query aborted: *Optional*. If set to yes, only return aborted orders, if no only unaborted orders. Do not give (or use "all")  to see all orders regardless of abort status.
  :query refunded: *Optional*. If set to yes, only return refunded orders, if no only unrefunded orders. Do not give (or use "all") to see all orders regardless of refund status.
  :query wired: *Optional*. If set to yes, only return wired orders, if no only orders with missing wire transfers. Do not give (or use "all") to see all orders regardless of wire transfer status.
  :query date: *Optional.* Time threshold, see ``delta`` for its interpretation.  Defaults to the oldest or most recent entry, depending on ``delta``.
  :query start: *Optional*. Row number threshold, see ``delta`` for its interpretation.  Defaults to ``UINT64_MAX``, namely the biggest row id possible in the database.
  :query delta: *Optional*. takes value of the form ``N (-N)``, so that at most ``N`` values strictly younger (older) than ``start`` and ``date`` are returned.  Defaults to ``-20``.
  :query timeout_ms: *Optional*. Timeout in milli-seconds to wait for additional orders if the answer would otherwise be negative (long polling). Only useful if delta is positive. Note that the merchant MAY still return a response that contains fewer than delta orders.

  **Response**

  :status 200 OK:
    The response is a JSON ``array`` of  `OrderHistory`.  The array is
    sorted such that entry ``i`` is younger than entry ``i+1``.

  .. ts:def:: OrderHistory

    interface OrderHistory {
      // The serial number this entry has in the merchant's DB.
      row_id: number;

      // order ID of the transaction related to this entry.
      order_id: string;

      // Transaction's timestamp
      timestamp: Timestamp;

      // Total amount the customer should pay for this order.
      total: Amount;

      // Total amount the customer did pay for this order.
      paid: Amount;

      // Total amount the customer was refunded for this order.
      // (includes abort-refund and refunds, boolean flag
      // below can help determine which case it is).
      refunded: Amount;

      // Was the order ever fully paid?
      is_paid: boolean;

    }




.. http:post:: /public/orders/$ORDER_ID/claim

  Wallet claims ownership (via nonce) over an order.  By claiming
  an order, the wallet obtains the full contract terms, and thereby
  implicitly also the hash of the contract terms it needs for the
  other ``/public/`` APIs to authenticate itself as the wallet that
  is indeed eligible to inspect this particular order's status.

  **Request**

  The request must be a `ClaimRequest`

  .. ts:def:: ClaimRequest

    interface ClaimRequest {
      // Nonce to identify the wallet that claimed the order.
      nonce: string;
    }

  **Response**

  :status 200 OK:
    The client has successfully claimed the order.
    The response contains the :ref:`contract terms <ContractTerms>`.
  :status 404 Not found:
    The backend is unaware of the instance or order.
  :status 409 Conflict:
    The someone else claimed the same order ID with different nonce before.


.. http:post:: /public/orders/$ORDER_ID/pay

  Pay for an order by giving a deposit permission for coins.  Typically used by
  the customer's wallet.  Note that this request does not include the
  usual ``h_contract`` argument to authenticate the wallet, as the hash of
  the contract is implied by the signatures of the coins.  Furthermore, this
  API doesn't really return useful information about the order.

  **Request:**

  The request must be a `pay request <PayRequest>`.

  **Response:**

  :status 200 OK:
    The exchange accepted all of the coins.
    The body is a `payment response <PaymentResponse>`.
    The ``frontend`` should now fullfill the contract.
  :status 400 Bad request:
    Either the client request is malformed or some specific processing error
    happened that may be the fault of the client as detailed in the JSON body
    of the response.
  :status 403 Forbidden:
    One of the coin signatures was not valid.
  :status 404 Not found:
    The merchant backend could not find the order or the instance
    and thus cannot process the payment.
  :status 409 Conflict:
    The exchange rejected the payment because a coin was already spent before.
    The response will include the ``coin_pub`` for which the payment failed,
    in addition to the response from the exchange to the ``/deposit`` request.
  :status 412 Precondition Failed:
    The given exchange is not acceptable for this merchant, as it is not in the
    list of accepted exchanges and not audited by an approved auditor.
  :status 424 Failed Dependency:
    The merchant's interaction with the exchange failed in some way.
    The client might want to try later again.
    This includes failures like the denomination key of a coin not being
    known to the exchange as far as the merchant can tell.

  The backend will return verbatim the error codes received from the exchange's
  :ref:`deposit <deposit>` API.  If the wallet made a mistake, like by
  double-spending for example, the frontend should pass the reply verbatim to
  the browser/wallet.  If the payment was successful, the frontend MAY use
  this to trigger some business logic.

  .. ts:def:: PaymentResponse

    interface PaymentResponse {
      // Signature on ``TALER_PaymentResponsePS`` with the public
      // key of the merchant instance.
      sig: EddsaSignature;

    }

  .. ts:def:: PayRequest

    interface PayRequest {
      coins: CoinPaySig[];
    }

  .. ts:def:: CoinPaySig

    export interface CoinPaySig {
      // Signature by the coin.
      coin_sig: string;

      // Public key of the coin being spend.
      coin_pub: string;

      // Signature made by the denomination public key.
      ub_sig: string;

      // The denomination public key associated with this coin.
      denom_pub: string;

      // The amount that is subtracted from this coin with this payment.
      contribution: Amount;

      // URL of the exchange this coin was withdrawn from.
      exchange_url: string;
    }


.. http:post:: /public/orders/$ORDER_ID/abort

  Abort paying for an order and obtain a refund for coins that
  were already deposited as part of a failed payment.

  **Request:**

  The request must be an `abort request <AbortRequest>`.

  :query h_contract: hash of the order's contract terms (this is used to authenticate the wallet/customer in case $ORDER_ID is guessable). *Mandatory!*

  **Response:**

  :status 200 OK:
    The exchange accepted all of the coins. The body is a
    a `merchant refund response <MerchantRefundResponse>`.
  :status 400 Bad request:
    Either the client request is malformed or some specific processing error
    happened that may be the fault of the client as detailed in the JSON body
    of the response.
  :status 403 Forbidden:
    The ``h_contract`` does not match the order.
  :status 404 Not found:
    The merchant backend could not find the order or the instance
    and thus cannot process the abort request.
  :status 412 Precondition Failed:
    Aborting the payment is not allowed, as the original payment did succeed.
  :status 424 Failed Dependency:
    The merchant's interaction with the exchange failed in some way.
    The error from the exchange is included.

  The backend will return verbatim the error codes received from the exchange's
  :ref:`refund <refund>` API.  The frontend should pass the replies verbatim to
  the browser/wallet.

  .. ts:def:: AbortRequest

    interface AbortRequest {
      // List of coins the wallet would like to see refunds for.
      // (Should be limited to the coins for which the original
      // payment succeeded, as far as the wallet knows.)
      coins: AbortedCoin[];
    }

    interface AbortedCoin {
      // Public key of a coin for which the wallet is requesting an abort-related refund.
      coin_pub: EddsaPublicKey;
    }



.. http:get:: /orders/$ORDER_ID/

  Merchant checks the payment status of an order.  If the order exists but is not payed
  yet, the response provides a redirect URL.  When the user goes to this URL,
  they will be prompted for payment.  Differs from the ``/public/`` API both
  in terms of what information is returned and in that the wallet must provide
  the contract hash to authenticate, while for this API we assume that the
  merchant is authenticated (as the endpoint is not ``/public/``).

  **Request:**

  :query session_id: *Optional*. Session ID that the payment must be bound to.  If not specified, the payment is not session-bound.
  :query transfer: *Optional*. If set to "YES", try to obtain the wire transfer status for this order from the exchange. Otherwise, the wire transfer status MAY be returned if it is available.
  :query timeout_ms: *Optional*. Timeout in milli-seconds to wait for a payment if the answer would otherwise be negative (long polling).

  **Response:**

  :status 200 OK:
    Returns a `MerchantOrderStatusResponse`, whose format can differ based on the status of the payment.
  :status 404 Not Found:
    The order or instance is unknown to the backend.
  :status 409 Conflict:
    The exchange previously claimed that a deposit was not included in a wire
    transfer, and now claims that it is.  This means that the exchange is
    dishonest.  The response contains the cryptographic proof that the exchange
    is misbehaving in the form of a `TransactionConflictProof`.
  :status 424 Failed dependency:
    We failed to obtain a response from the exchange about the
    wire transfer status.

  .. ts:def:: MerchantOrderStatusResponse

    type MerchantOrderStatusResponse = CheckPaymentPaidResponse | CheckPaymentUnpaidResponse

  .. ts:def:: CheckPaymentPaidResponse

    interface CheckPaymentPaidResponse {
      paid: true;

      // Was the payment refunded (even partially)
      refunded: boolean;

      // Amount that was refunded, only present if refunded is true.
      refund_amount?: Amount;

      // Contract terms
      contract_terms: ContractTerms;

      // If available, the wire transfer status from the exchange for this order
      wire_details?: TransactionWireTransfer;
    }

  .. ts:def:: CheckPaymentUnpaidResponse

    interface CheckPaymentUnpaidResponse {
      paid: false;

      // URI that the wallet must process to complete the payment.
      taler_pay_uri: string;

      // Alternative order ID which was paid for already in the same session.
      // Only given if the same product was purchased before in the same session.
      already_paid_order_id?: string;

      // FIXME: why do we NOT return the contract terms here?
    }

  .. ts:def:: TransactionWireTransfer

    interface TransactionWireTransfer {

      // Responsible exchange
      exchange_uri: string;

      // 32-byte wire transfer identifier
      wtid: Base32;

      // execution time of the wire transfer
      execution_time: Timestamp;

      // Total amount that has been wire transfered
      // to the merchant
      amount: Amount;
    }

  .. ts:def:: TransactionConflictProof

    interface TransactionConflictProof {
      // Numerical `error code <error-codes>`
      code: number;

      // Human-readable error description
      hint: string;

      // A claim by the exchange about the transactions associated
      // with a given wire transfer; it does not list the
      // transaction that ``transaction_tracking_claim`` says is part
      // of the aggregate.  This is
      // a ``/track/transfer`` response from the exchange.
      wtid_tracking_claim: TrackTransferResponse;

      // The current claim by the exchange that the given
      // transaction is included in the above WTID.
      // (A response from ``/track/order``).
      transaction_tracking_claim: TrackTransactionResponse;

      // Public key of the coin for which we got conflicting information.
      coin_pub: CoinPublicKey;

    }


.. http:get:: /public/orders/$ORDER_ID/

  Query the payment status of an order. This endpoint is for the wallet.
  When the wallet goes to this URL and it is unpaid,
  they will be prompted for payment.

  // FIXME: note that this combines the previous APIs
  // to check-payment and to obtain refunds.

  **Request**

  :query h_contract: hash of the order's contract terms (this is used to authenticate the wallet/customer in case $ORDER_ID is guessable). *Mandatory!*
  :query session_id: *Optional*. Session ID that the payment must be bound to.  If not specified, the payment is not session-bound.
  :query timeout_ms: *Optional.*  If specified, the merchant backend will
    wait up to ``timeout_ms`` milliseconds for completion of the payment before
    sending the HTTP response.  A client must never rely on this behavior, as the
    merchant backend may return a response immediately.
  :query refund=AMOUNT: *Optional*. Indicates that we are polling for a refund above the given AMOUNT. Only useful in combination with timeout.

  **Response**

  :status 200 OK:
    The response is a `PublicPayStatusResponse`, with ``paid`` true.
    FIXME: what about refunded?
  :status 402 Payment required:
    The response is a `PublicPayStatusResponse`, with ``paid`` false.
    FIXME: what about refunded?
  :status 403 Forbidden:
    The ``h_contract`` does not match the order.
  :status 404 Not found:
    The merchant backend is unaware of the order.

  .. ts:def:: PublicPayStatusResponse

    interface PublicPayStatusResponse {
      // Has the payment for this order (ever) been completed?
      paid: boolean;

      // Was the payment refunded (even partially, via refund or abort)?
      refunded: boolean;

      // Amount that was refunded in total.
      refund_amount: Amount;

      // Refunds for this payment, empty array for none.
      refunds: RefundDetail[];

      // URI that the wallet must process to complete the payment.
      taler_pay_uri: string;

      // Alternative order ID which was paid for already in the same session.
      // Only given if the same product was purchased before in the same session.
      already_paid_order_id?: string;

    }


.. http:delete:: /orders/$ORDER_ID

  Delete information about an order.  Fails if the order was paid in the
  last 10 years (or whatever TAX_RECORD_EXPIRATION is set to) or was
  claimed but is unpaid and thus still a valid offer.

  **Response**

  :status 204 No content:
    The backend has successfully deleted the order.
  :status 404 Not found:
    The backend does not know the instance or the order.
  :status 409 Conflict:
    The backend refuses to delete the order.


--------------
Giving Refunds
--------------


.. http:post:: /orders/$ORDER_ID/refund

  Increase the refund amount associated with a given order.  The user should be
  redirected to the ``taler_refund_url`` to trigger refund processing in the wallet.

  **Request**

  The request body is a `RefundRequest` object.

  **Response**

  :status 200 OK:
    The refund amount has been increased, the backend responds with a `MerchantRefundResponse`
  :status 404 Not found:
    The order is unknown to the merchant
  :status 409 Conflict:
    The refund amount exceeds the amount originally paid

  .. ts:def:: RefundRequest

    interface RefundRequest {
      // Amount to be refunded
      refund: Amount;

      // Human-readable refund justification
      reason: string;
    }

  .. ts:def:: MerchantRefundResponse

    interface MerchantRefundResponse {

      // Hash of the contract terms of the contract that is being refunded.
      // FIXME: why do we return this?
      h_contract_terms: HashCode;

      // URL (handled by the backend) that the wallet should access to
      // trigger refund processing.
      // FIXME: isn't this basically now always ``/public/orders/$ORDER_ID/``?
      // If so, why return this?
      taler_refund_url: string;
    }



------------------------
Tracking Wire Transfers
------------------------

.. http:post:: /check-transfer

  Inform the backend over an incoming wire transfer. The backend should inquire about the details with the exchange and mark the respective orders as wired.

  **Request:**

   The request must provide `transfer information <TransferInformation>`.

  **Response:**

  :status 200 OK:
    The wire transfer is known to the exchange, details about it follow in the body.
    The body of the response is a `TrackTransferResponse`.  Note that
    the similarity to the response given by the exchange for a /track/transfer
    is completely intended.

  :status 404 Not Found:
    The wire transfer identifier is unknown to the exchange.

  :status 424 Failed Dependency: The exchange provided conflicting information about the transfer. Namely,
    there is at least one deposit among the deposits aggregated by ``wtid`` that accounts for a coin whose
    details don't match the details stored in merchant's database about the same keyed coin.
    The response body contains the `TrackTransferConflictDetails`.

  .. ts:def:: TransferInformation

    interface TransferInformation {
      // how much was wired to the merchant (minus fees)
      credit_amount: Amount;

      // raw wire transfer identifier identifying the wire transfer (a base32-encoded value)
      wtid: FIXME;

      // name of the wire transfer method used for the wire transfer
      // FIXME: why not a payto URI?
      wire_method;

      // base URL of the exchange that made the wire transfer
      exchange: string;
    }

  .. ts:def:: TrackTransferResponse

    interface TrackTransferResponse {
      // Total amount transferred
      total: Amount;

      // Applicable wire fee that was charged
      wire_fee: Amount;

      // public key of the merchant (identical for all deposits)
      // FIXME: why return this?
      merchant_pub: EddsaPublicKey;

      // hash of the wire details (identical for all deposits)
      // FIXME: why return this? Isn't this the WTID!?
      h_wire: HashCode;

      // Time of the execution of the wire transfer by the exchange, according to the exchange
      execution_time: Timestamp;

      // details about the deposits
      deposits_sums: TrackTransferDetail[];

      // signature from the exchange made with purpose
      // ``TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT``
      // FIXME: why return this?
      exchange_sig: EddsaSignature;

      // public EdDSA key of the exchange that was used to generate the signature.
      // Should match one of the exchange's signing keys from /keys.  Again given
      // explicitly as the client might otherwise be confused by clock skew as to
      // which signing key was used.
      // FIXME: why return this?
      exchange_pub: EddsaSignature;
    }

  .. ts:def:: TrackTransferDetail

    interface TrackTransferDetail {
      // Business activity associated with the wire transferred amount
      // ``deposit_value``.
      order_id: string;

      // The total amount the exchange paid back for ``order_id``.
      deposit_value: Amount;

      // applicable fees for the deposit
      deposit_fee: Amount;
    }


  **Details:**

  .. ts:def:: TrackTransferConflictDetails

    interface TrackTransferConflictDetails {
      // Numerical `error code <error-codes>`
      code: number;

      // Text describing the issue for humans.
      hint: string;

      // A /deposit response matching ``coin_pub`` showing that the
      // exchange accepted ``coin_pub`` for ``amount_with_fee``.
      exchange_deposit_proof: DepositSuccess;

      // Offset in the ``exchange_transfer_proof`` where the
      // exchange's response fails to match the ``exchange_deposit_proof``.
      conflict_offset: number;

      // The response from the exchange which tells us when the
      // coin was returned to us, except that it does not match
      // the expected value of the coin.
      exchange_transfer_proof: TrackTransferResponse;

      // Public key of the coin for which we have conflicting information.
      coin_pub: EddsaPublicKey;

      // Merchant transaction in which ``coin_pub`` was involved for which
      // we have conflicting information.
      transaction_id: number;

      // Expected value of the coin.
      amount_with_fee: Amount;

      // Expected deposit fee of the coin.
      deposit_fee: Amount;

    }


.. http:get:: /transfers

  Obtain a list of all wire transfers the backend has checked.

  **Request:**

   :query filter: FIXME: should have a way to filter, maybe even long-poll?

  **Response:**

  FIXME: to be specified.



--------------------
Giving Customer Tips
--------------------


.. http:post:: /create-reserve

  Create a reserve for tipping.

  **Request:**

  The request body is a `ReserveCreateRequest` object.

  **Response:**

  :status 200 OK:
    The backend is waiting for the reserve to be established. The merchant
    must now perform the wire transfer indicated in the `ReserveCreateConfirmation`.
  :status 424 Failed Depencency:
    We could not obtain /wire details from the specified exchange base URL.

  .. ts:def:: ReserveCreateRequest

    interface ReserveCreateRequest {
      // Amount that the merchant promises to put into the reserve
      initial_amount: Amount;

      // Exchange the merchant intends to use for tipping
      exchange_base_url: string;

    }

  .. ts:def:: ReserveCreateConfirmation

    interface ReserveCreateConfirmation {
      // Public key identifying the reserve
      reserve_pub: EddsaPublicKey;

      // Wire account of the exchange where to transfer the funds
      payto_url: string;

    }

.. http:get:: /reserves

   Obtain list of reserves that have been created for tipping.

   **Request:**

   :query after: *Optional*.  Only return reserves created after the given timestamp [FIXME: unit?]

   **Response:**

  :status 200 OK:
    Returns a list of known tipping reserves.
    The body is a `TippingReserveStatus`.

  .. ts:def:: TippingReserveStatus

    interface TippingReserveStatus {

      // Array of all known reserves (possibly empty!)
      reserves: ReserveStatusEntry[];

    }

  .. ts:def:: ReserveStatusEntry

     interface ReserveStatusEntry {

      // Public key of the reserve
      reserve_pub: EddsaPublicKey;

      // Timestamp when it was established
      creation_time: Timestamp;

      // Timestamp when it expires
      expiration_time: Timestamp;

      // Initial amount as per reserve creation call
      merchant_initial_amount: Amount;

      // Initial amount as per exchange, 0 if exchange did
      // not confirm reserve creation yet.
      exchange_initial_amount: Amount;

      // Amount picked up so far.
      pickup_amount: Amount;

      // Amount approved for tips that exceeds the pickup_amount.
      committed_amount: Amount;

    }


.. http:get:: /reserves/$RESERVE_PUB

   Obtain information about a specific reserve that have been created for tipping.

   **Request:**

   :query tips: *Optional*. If set to "yes", returns also information about all of the tips created

   **Response:**

  :status 200 OK:
    Returns the `ReserveDetail`.
  :status 404 Not found:
    The tipping reserve is not known.
  :status 424 Failed Dependency:
    We are having trouble with the request because of a problem with the exchange.
    Likely returned with an "exchange_code" in addition to a "code" and
    an "exchange_http_status" in addition to our own HTTP status. Also usually
    includes the full exchange reply to our request under "exchange_reply".
    This is only returned if there was actual trouble with the exchange, not
    if the exchange merely did not respond yet or if it responded that the
    reserve was not yet filled.

  .. ts:def:: ReserveDetail

    interface ReserveDetail {

      // Timestamp when it was established
      creation_time: Timestamp;

      // Timestamp when it expires
      expiration_time: Timestamp;

      // Initial amount as per reserve creation call
      merchant_initial_amount: Amount;

      // Initial amount as per exchange, 0 if exchange did
      // not confirm reserve creation yet.
      exchange_initial_amount: Amount;

      // Amount picked up so far.
      pickup_amount: Amount;

      // Amount approved for tips that exceeds the pickup_amount.
      committed_amount: Amount;

      // Array of all tips created by this reserves (possibly empty!).
      // Only present if asked for explicitly.
      tips?: TipStatusEntry[];

    }

  .. ts:def:: TipStatusEntry

    interface TipStatusEntry {

      // Unique identifier for the tip
      tip_id: HashCode;

      // Total amount of the tip that can be withdrawn.
      total_amount: Amount;

      // Human-readable reason for why the tip was granted.
      reason: String;

    }


.. http:post:: /reserves/$RESERVE_PUB/authorize-tip

  Authorize creation of a tip from the given reserve.

  **Request:**

  The request body is a `TipCreateRequest` object.

  **Response**

  :status 200 OK:
    A tip has been created. The backend responds with a `TipCreateConfirmation`
  :status 404 Not Found:
    The instance or the reserve is unknown to the backend.
  :status 412 Precondition Failed:
    The tip amount requested exceeds the available reserve balance for tipping.

  .. ts:def:: TipCreateRequest

    interface TipCreateRequest {
      // Amount that the customer should be tipped
      amount: Amount;

      // Justification for giving the tip
      justification: string;

      // URL that the user should be directed to after tipping,
      // will be included in the tip_token.
      next_url: string;
    }

  .. ts:def:: TipCreateConfirmation

    interface TipCreateConfirmation {
      // Unique tip identifier for the tip that was created.
      tip_id: HashCode;

      // Token that will be handed to the wallet,
      // contains all relevant information to accept
      // a tip.
      tip_token: string;

      // URL that will directly trigger processing
      // the tip when the browser is redirected to it
      tip_redirect_url: string;

    }


.. http:delete:: /reserves/$RESERVE_PUB

  Delete information about a reserve.  Fails if the reserve still has
  committed to tips that were not yet picked up and that have not yet
  expired.

  **Response**

  :status 204 No content:
    The backend has successfully deleted the reserve.
  :status 404 Not found:
    The backend does not know the instance or the reserve.
  :status 409 Conflict:
    The backend refuses to delete the reserve (committed tips).



.. http:get:: /tips/$TIP_ID

  Obtain information about a particular tip.

   **Request:**

   :query pickups: if set to "yes", returns also information about all of the pickups

  **Response**

  :status 200 OK:
    The tip is known. The backend responds with a `TipDetails` message
  :status 404 Not Found:
    The tip is unknown to the backend.

  .. ts:def:: TipDetails

    interface TipDetails {

      // Amount that we authorized for this tip.
      total_authorized: Amount;

      // Amount that was picked up by the user already.
      total_picked_up: Amount;

      // Human-readable reason given when authorizing the tip.
      reason: String;

      // Timestamp indicating when the tip is set to expire (may be in the past).
      expiration: Timestamp;

      // Reserve public key from which the tip is funded
      reserve_pub: EddsaPublicKey;

      // Array showing the pickup operations of the wallet (possibly empty!).
      // Only present if asked for explicitly.
      pickups?: PickupDetail[];
    }

  .. ts:def:: PickupDetail

    interface PickupDetail {

      // Unique identifier for the pickup operation.
      pickup_id: HashCode;

      // Number of planchets involved.
      num_planchets: integer;

      // Total amount requested for this pickup_id.
      requested_amount: Amount;

      // Total amount processed by the exchange for this pickup.
      exchange_amount: Amount;

    }


.. http:post:: /public/tips/$TIP_ID/pickup

  Handle request from wallet to pick up a tip.

  **Request**

  The request body is a `TipPickupRequest` object.

  **Response**

  :status 200 OK:
    A tip is being returned. The backend responds with a `TipResponse`
  :status 401 Unauthorized:
    The tip amount requested exceeds the tip.
  :status 404 Not Found:
    The tip identifier is unknown.
  :status 409 Conflict:
    Some of the denomination key hashes of the request do not match those currently available from the exchange (hence there is a conflict between what the wallet requests and what the merchant believes the exchange can provide).

  .. ts:def:: TipPickupRequest

    interface TipPickupRequest {

      // Identifier of the tip.
      tip_id: HashCode;

      // List of planches the wallet wants to use for the tip
      planchets: PlanchetDetail[];
    }

  .. ts:def:: PlanchetDetail

    interface PlanchetDetail {
      // Hash of the denomination's public key (hashed to reduce
      // bandwidth consumption)
      denom_pub_hash: HashCode;

      // coin's blinded public key
      coin_ev: CoinEnvelope;

    }

  .. ts:def:: TipResponse

    interface TipResponse {

      // Blind RSA signatures over the planchets.
      // The order of the signatures matches the planchets list.
      blind_sigs: BlindSignature[];
    }

    interface BlindSignature {

      // The (blind) RSA signature. Still needs to be unblinded.
      blind_sig: RsaSignature;
    }





-------------------------
Dynamic Merchant Instance
-------------------------

.. note::

    The endpoints to dynamically manage merchant instances has not been
    implemented yet. The bug id for this reference is #5349.

.. http:get:: /instances

  This is used to return the list of all the merchant instances

  **Response**

  :status 200 OK:
    The backend has successfully returned the list of instances stored. Returns
    a `InstancesResponse`.

  .. ts:def:: InstancesResponse

    interface InstancesResponse {
      // List of instances that are present in the backend (see `Instance`)
      instances: Instance[];
    }

  The `Instance` object describes the instance registered with the backend. It has the following structure:

  .. ts:def:: Instance

    interface Instance {
      // Merchant name corresponding to this instance.
      name: string;

      // The URL where the wallet will send coins.
      payto: string;

      // Merchant instance of the response to create
      instance: string;

      //unique key for each merchant
      merchant_id: string;
    }


.. http:put:: /instances/$INSTANCE

  This request will be used to create a new merchant instance in the backend.

  **Request**

  The request must be a `CreateInstanceRequest`.

  **Response**

  :status 200 OK:
    The backend has successfully created the instance.  The response is a
    `CreateInstanceResponse`.

  .. ts:def:: CreateInstanceRequest

    interface CreateInstanceRequest {
      // The URL where the wallet has to send coins.
      // payto://-URL of the merchant's bank account. Required.
      // FIXME: need an array, and to distinguish between
      // supported and active (see taler.conf options on accounts!)
      payto: string;

      // Merchant instance of the response to create
      // This field is optional. If it is not specified
      // then it will automatically be created.
      // FIXME: I do not understand this argument. -CG
      instance?: string;

      // Merchant name corresponding to this instance.
      name: string;

    }

  .. ts:def:: CreateInstanceResponse

    interface CreateInstanceResponse {
      // Merchant instance of the response that was created
      // FIXME: I do not understand this value, isn't it implied?
      instance: string;

      //unique key for each merchant
      // FIXME: I do not understand this value.
      merchant_id: string;
    }


.. http:get:: /instances/<instance-id>

  This is used to query a specific merchant instance.

  **Request:**

  :query instance_id: instance id that should be used for the instance

  **Response**

  :status 200 OK:
    The backend has successfully returned the list of instances stored. Returns
    a `QueryInstancesResponse`.

  .. ts:def:: QueryInstancesResponse

    interface QueryInstancesResponse {
      // The URL where the wallet has to send coins.
      // payto://-URL of the merchant's bank account. Required.
      payto: string;

      // Merchant instance of the response to create
      // This field is optional. If it is not specified
      // then it will automatically be created.
      instance?: string;

      // Merchant name corresponding to this instance.
      name: string;

      // Public key of the merchant/instance, in Crockford Base32 encoding.
      merchant_pub: EddsaPublicKey;

      // List of the payment targets supported by this instance. Clients can
      // specify the desired payment target in /order requests.  Note that
      // front-ends do not have to support wallets selecting payment targets.
      payment_targets: string[];

    }


.. http:post:: /instances/<instance-id>

  This request will be used to update merchant instance in the backend.


  **Request**

  The request must be a `PostInstanceUpdateRequest`.

  **Response**

  :status 200 OK:
    The backend has successfully updated the instance.  The response is a
    `PostInstanceUpdateResponse`.

  .. ts:def:: PostInstanceUpdateRequest

    interface PostInstanceUpdateRequest {
      // Merchant instance that is to be updaated. Required.
      instance: string;

      // New URL where the wallet has to send coins.
      // payto://-URL of the merchant's bank account. Required.
      payto: string;

      // Merchant name coreesponding to this instance.
      name: string;

    }

  .. ts:def:: PostInstanceUpdateResponse

    interface PostInstanceUpdateResponse {
      // Merchant instance of the response that was updated
      instance: string;

      //unique key for each merchant
      merchant_id: string;
    }


.. http:delete:: /instances/<instance-id>

  This request will be used to delete merchant instance in the backend.

  **Request:**

  :query instance_id: instance id that should be used for the instance

  **Response**

  :status 200 OK:
    The backend has successfully removed the instance.  The response is a
    `PostInstanceRemoveResponse`.

  .. ts:def:: PostInstanceRemoveResponse

    interface PostInstanceRemoveResponse {
      deleted: true;
    }


------------------
The Contract Terms
------------------

The contract terms must have the following structure:

  .. ts:def:: ContractTerms

    interface ContractTerms {
      // Human-readable description of the whole purchase
      summary: string;

      // Map from IETF BCP 47 language tags to localized summaries
      summary_i18n?: { [lang_tag: string]: string };

      // Unique, free-form identifier for the proposal.
      // Must be unique within a merchant instance.
      // For merchants that do not store proposals in their DB
      // before the customer paid for them, the order_id can be used
      // by the frontend to restore a proposal from the information
      // encoded in it (such as a short product identifier and timestamp).
      order_id: string;

      // Total price for the transaction.
      // The exchange will subtract deposit fees from that amount
      // before transferring it to the merchant.
      amount: Amount;

      // The URL for this purchase.  Every time is is visited, the merchant
      // will send back to the customer the same proposal.  Clearly, this URL
      // can be bookmarked and shared by users.
      fulfillment_url: string;

      // Maximum total deposit fee accepted by the merchant for this contract
      max_fee: Amount;

      // Maximum wire fee accepted by the merchant (customer share to be
      // divided by the 'wire_fee_amortization' factor, and further reduced
      // if deposit fees are below 'max_fee').  Default if missing is zero.
      max_wire_fee: Amount;

      // Over how many customer transactions does the merchant expect to
      // amortize wire fees on average?  If the exchange's wire fee is
      // above 'max_wire_fee', the difference is divided by this number
      // to compute the expected customer's contribution to the wire fee.
      // The customer's contribution may further be reduced by the difference
      // between the 'max_fee' and the sum of the actual deposit fees.
      // Optional, default value if missing is 1.  0 and negative values are
      // invalid and also interpreted as 1.
      wire_fee_amortization: number;

      // List of products that are part of the purchase (see `Product`).
      products: Product[];

      // Time when this contract was generated
      timestamp: Timestamp;

      // After this deadline has passed, no refunds will be accepted.
      refund_deadline: Timestamp;

      // After this deadline, the merchant won't accept payments for the contact
      pay_deadline: Timestamp;

      // Transfer deadline for the exchange.  Must be in the
      // deposit permissions of coins used to pay for this order.
      wire_transfer_deadline: Timestamp;

      // Merchant's public key used to sign this proposal; this information
      // is typically added by the backend Note that this can be an ephemeral key.
      merchant_pub: EddsaPublicKey;

      // Base URL of the (public!) merchant backend API.
      // Must be an absolute URL that ends with a slash.
      merchant_base_url: string;

      // More info about the merchant, see below
      merchant: Merchant;

      // The hash of the merchant instance's wire details.
      h_wire: HashCode;

      // Wire transfer method identifier for the wire method associated with h_wire.
      // The wallet may only select exchanges via a matching auditor if the
      // exchange also supports this wire method.
      // The wire transfer fees must be added based on this wire transfer method.
      wire_method: string;

      // Any exchanges audited by these auditors are accepted by the merchant.
      auditors: Auditor[];

      // Exchanges that the merchant accepts even if it does not accept any auditors that audit them.
      exchanges: Exchange[];

      // Map from labels to locations
      locations: { [label: string]: [location: Location], ... };

      // Nonce generated by the wallet and echoed by the merchant
      // in this field when the proposal is generated.
      nonce: string;

      // Specifies for how long the wallet should try to get an
      // automatic refund for the purchase. If this field is
      // present, the wallet should wait for a few seconds after
      // the purchase and then automatically attempt to obtain
      // a refund.  The wallet should probe until "delay"
      // after the payment was successful (i.e. via long polling
      // or via explicit requests with exponential back-off).
      //
      // In particular, if the wallet is offline
      // at that time, it MUST repeat the request until it gets
      // one response from the merchant after the delay has expired.
      // If the refund is granted, the wallet MUST automatically
      // recover the payment.  This is used in case a merchant
      // knows that it might be unable to satisfy the contract and
      // desires for the wallet to attempt to get the refund without any
      // customer interaction.  Note that it is NOT an error if the
      // merchant does not grant a refund.
      auto_refund?: RelativeTime;

      // Extra data that is only interpreted by the merchant frontend.
      // Useful when the merchant needs to store extra information on a
      // contract without storing it separately in their database.
      extra?: any;
    }

  The wallet must select a exchange that either the merchant accepts directly by
  listing it in the exchanges array, or for which the merchant accepts an auditor
  that audits that exchange by listing it in the auditors array.

  The `Product` object describes the product being purchased from the merchant. It has the following structure:

  .. ts:def:: Product

    interface Product {
      // Human-readable product description.
      description: string;

      // Map from IETF BCP 47 language tags to localized descriptions
      description_i18n?: { [lang_tag: string]: string };

      // The quantity of the product to deliver to the customer (optional, if applicable)
      quantity?: string;

      // The price of the product; this is the total price for the amount specified by 'quantity'
      price: Amount;

      // merchant-internal identifier for the product
      product_id?: string;

      // An optional base64-encoded product image
      image?: ImageDataUrl;

      // a list of objects indicating a 'taxname' and its amount. Again, italics denotes the object field's name.
      taxes?: any[];

      // time indicating when this product should be delivered
      delivery_date: Timestamp;

      // where to deliver this product. This may be an URL for online delivery
      // (i.e. 'http://example.com/download' or 'mailto:customer@example.com'),
      // or a location label defined inside the proposition's 'locations'.
      // The presence of a colon (':') indicates the use of an URL.
      delivery_location: string;
    }

  .. ts:def:: Merchant

    interface Merchant {
      // label for a location with the business address of the merchant
      address: string;

      // the merchant's legal name of business
      name: string;

      // label for a location that denotes the jurisdiction for disputes.
      // Some of the typical fields for a location (such as a street address) may be absent.
      jurisdiction: string;
    }


  .. ts:def:: Location

    interface Location {
      country?: string;
      city?: string;
      state?: string;
      region?: string;
      province?: string;
      zip_code?: string;
      street?: string;
      street_number?: string;
    }

  .. ts:def:: Auditor

    interface Auditor {
      // official name
      name: string;

      // Auditor's public key
      auditor_pub: EddsaPublicKey;

      // Base URL of the auditor
      url: string;
    }

  .. ts:def:: Exchange

    interface Exchange {
      // the exchange's base URL
      url: string;

      // master public key of the exchange
      master_pub: EddsaPublicKey;
    }