summaryrefslogtreecommitdiff
path: root/core/api-merchant.rst
diff options
context:
space:
mode:
Diffstat (limited to 'core/api-merchant.rst')
-rw-r--r--core/api-merchant.rst2030
1 files changed, 1359 insertions, 671 deletions
diff --git a/core/api-merchant.rst b/core/api-merchant.rst
index d197c07e..53ccdae6 100644
--- a/core/api-merchant.rst
+++ b/core/api-merchant.rst
@@ -1,6 +1,6 @@
..
This file is part of GNU TALER.
- Copyright (C) 2014-2020 Taler Systems SA
+ Copyright (C) 2014-2024 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -16,14 +16,16 @@
@author Marcello Stanisci
@author Florian Dold
@author Christian Grothoff
+ @author Priscilla Huang
.. _merchant-api:
-====================
-Merchant Backend API
-====================
+============================
+Merchant Backend RESTful API
+============================
.. contents:: Table of Contents
+ :local:
-----------------------
Base URLs and Instances
@@ -34,11 +36,13 @@ This is useful when multiple businesses want to share the same payment
infrastructure.
Merchant backends have one special ``default`` instance. This ``default``
-instance is used when no explicit instance is specified. Despite its name,
-this instance must be created after the installation. In case *no* default
-instance is found - or its credentials got lost -, the administrator can use
-the default instance's rights by resorting on the ``--auth`` command line option,
-or by restarting the service by providing an environment variable called
+instance is used when no explicit instance is specified. Note that using
+``/instances/default/$ANYTHING`` is deprecated and will result in a permanent
+redirect (HTTP status 308) to ``$ANYTHING``. a Despite its name, this instance
+must be created after the installation. In case *no* default instance is
+found - or its credentials got lost -, the administrator can use the default
+instance's rights by resorting on the ``--auth`` command line option, or by
+restarting the service by providing an environment variable called
``TALER_MERCHANT_TOKEN``.
Each instance (default and others) has a base URL. The resources under
@@ -72,10 +76,10 @@ Examples:
https://merchant-backend.example.com/instances/myinst/orders/ABCD
A private endpoint (explicit "default" instance):
- https://merchant-backend.example.com/instances/default/private/orders
+ https://merchant-backend.example.com/private/orders
A public endpoint (explicit "default" instance):
- https://merchant-backend.example.com/instances/default/orders
+ https://merchant-backend.example.com/orders
Endpoints to manage other instances (ONLY for implicit "default" instance):
https://merchant-backend.example.com/management/instances
@@ -97,12 +101,17 @@ Authentication
Each merchant instance has separate authentication settings for the private API resources
of that instance.
-Currently, the API supports two auth methods:
+Currently, the API supports two main authentication methods:
* ``external``: With this method, no checks are done by the merchant backend.
Instead, a reverse proxy / API gateway must do all authentication/authorization checks.
* ``token``: With this method, the client must provide a ``Authorization: Bearer $TOKEN``
- header, where ``$TOKEN`` is a secret authentication token configured for the instance.
+ header, where ``$TOKEN`` is a secret authentication token configured for the instance which must begin with the RFC 8959 prefix.
+
+Additionally, clients can send a **login token** which they may obtain from
+the ``/private/token`` endpoint. Such a login token is valid only for a
+limited period of time and can be used by clients to avoid storing the
+long-term login secrets from an authentication method.
-----------------
Configuration API
@@ -114,6 +123,7 @@ such as the implemented version of the protocol and the currency used.
.. http:get:: /config
Return the protocol version and currency supported by this merchant backend.
+ This specification corresponds to ``current`` protocol being version **13**.
**Response:**
@@ -131,9 +141,53 @@ such as the implemented version of the protocol and the currency used.
// Name of the protocol.
name: "taler-merchant";
- // Currency supported by this backend.
+ // URN of the implementation (needed to interpret 'revision' in version).
+ // @since **v8**, may become mandatory in the future.
+ implementation?: string;
+
+ // Default (!) currency supported by this backend.
+ // This is the currency that the backend should
+ // suggest by default to the user when entering
+ // amounts. See ``currencies`` for a list of
+ // supported currencies and how to render them.
currency: string;
+ // How services should render currencies supported
+ // by this backend. Maps
+ // currency codes (e.g. "EUR" or "KUDOS") to
+ // the respective currency specification.
+ // All currencies in this map are supported by
+ // the backend. Note that the actual currency
+ // specifications are a *hint* for applications
+ // that would like *advice* on how to render amounts.
+ // Applications *may* ignore the currency specification
+ // if they know how to render currencies that they are
+ // used with.
+ currencies: { currency : CurrencySpecification};
+
+ // Array of exchanges trusted by the merchant.
+ // Since protocol **v6**.
+ exchanges: ExchangeConfigInfo[];
+
+ }
+
+ .. ts:def:: ExchangeConfigInfo
+
+ interface ExchangeConfigInfo {
+
+ // Base URL of the exchange REST API.
+ base_url: string;
+
+ // Currency for which the merchant is configured
+ // to trust the exchange.
+ // May not be the one the exchange actually uses,
+ // but is the only one we would trust this exchange for.
+ currency: string;
+
+ // Offline master public key of the exchange. The
+ // ``/keys`` data must be signed with this public
+ // key for us to trust it.
+ master_pub: EddsaPublicKey;
}
@@ -145,8 +199,7 @@ This section describes (public) endpoints that wallets must be able
to interact with directly (without HTTP-based authentication). These
endpoints are used to process payments (claiming an order, paying
for the order, checking payment/refund status and aborting payments),
-process refunds (checking refund status, obtaining the refund),
-and to pick up tips.
+and to process refunds (checking refund status, obtaining the refund).
Claiming an order
@@ -235,24 +288,26 @@ Making the payment
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.
+ This includes the case where the payment is insufficient (sum is below
+ the required total amount, for example because the wallet calculated the
+ fees wrong).
:http:statuscode:`402 Payment required`:
There used to be a sufficient payment, but due to refunds the amount effectively
paid is no longer sufficient. (If the amount is generally insufficient, we
- return "406 Not Acceptable", only if this is because of refunds we return 402.)
+ return "400 Bad Request", only if this is because of refunds we return 402.)
:http:statuscode:`403 Forbidden`:
One of the coin signatures was not valid.
:http:statuscode:`404 Not found`:
The merchant backend could not find the order or the instance and thus cannot process the payment.
- :http:statuscode:`406 Not acceptable`:
- The payment is insufficient (sum is below the required total amount).
- TODO: Should probably change to a different status code in the future as 406 is technically wrong.
:http:statuscode:`408 Request timeout`:
The backend took too long to process the request. Likely the merchant's connection
to the exchange timed out. Try again.
:http:statuscode:`409 Conflict`:
- The exchange rejected the payment because a coin was already spent, or
+ The exchange rejected the payment because a coin was already spent (or
+ used in a different way for the same purchase previously), or
the merchant rejected the payment because the order was already fully paid
- (and then return signatures with refunds). If a coin was already spent,
+ (and then return signatures with refunds). If a coin was already spent
+ (this includes re-using the same coin after a refund),
the response will include the ``exchange_url`` for which the payment failed,
in addition to the response from the exchange to the ``/batch-deposit`` request.
:http:statuscode:`410 Gone`:
@@ -283,6 +338,10 @@ Making the payment
// key of the merchant instance.
sig: EddsaSignature;
+ // Text to be shown to the point-of-sale staff as a proof of
+ // payment.
+ pos_confirmation?: string;
+
}
.. ts:def:: PayRequest
@@ -291,6 +350,9 @@ Making the payment
// The coins used to make the payment.
coins: CoinPaySig[];
+ // Custom inputs from the wallet for the contract.
+ wallet_data?: Object;
+
// The session for which the payment is made (or replayed).
// Only set for session-based payments.
session_id?: string;
@@ -307,7 +369,7 @@ Making the payment
coin_pub: EddsaPublicKey;
// Signature made by the denomination public key.
- ub_sig: RsaSignature;
+ ub_sig: UnblindedSignature;
// The hash of the denomination public key associated with this coin.
h_denom: HashCode;
@@ -350,6 +412,7 @@ Querying payment status
merchant backend may return a response immediately.
:query await_refund_obtained=BOOLEAN: *Optional*. If set to "yes", poll for the order's pending refunds to be picked up. ``timeout_ms`` specifies how long we will wait for the refund.
:query refund=AMOUNT: *Optional*. Indicates that we are polling for a refund above the given AMOUNT. ``timeout_ms`` will specify how long we will wait for the refund.
+ :query allow_refunded_for_repurchase: *Optional*. Since protocol **v9** refunded orders are only returned under "already_paid_order_id" if this flag is set explicitly to "YES".
**Response:**
@@ -449,10 +512,10 @@ again.
**Response:**
- :http:statuscode:`204 No content`:
+ :http:statuscode:`200 Ok`:
The merchant accepted the signature.
The ``frontend`` should now fulfill the contract.
- Note that it is possible that refunds have been granted.
+ Note that it is possible that refunds have been granted. Response is of type `PaidRefundStatusResponse`.
:http:statuscode:`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
@@ -463,6 +526,21 @@ again.
The merchant backend could not find the order or the instance
and thus cannot process the request.
+ **Details**:
+
+ .. ts:def:: PaidRefundStatusResponse
+
+ interface PaidRefundStatusResponse {
+
+ // Text to be shown to the point-of-sale staff as a proof of
+ // payment (present only if re-usable OTP algorithm is used).
+ pos_confirmation?: string;
+
+ // True if the order has been subjected to
+ // refunds. False if it was simply paid.
+ refunded: boolean;
+ }
+
.. ts:def:: PaidRequest
interface PaidRequest {
@@ -475,6 +553,9 @@ again.
// database access).
h_contract: HashCode;
+ // Hash over custom inputs from the wallet for the contract.
+ wallet_data_hash?: HashCode;
+
// Session id for which the payment is proven.
session_id: string;
}
@@ -511,7 +592,7 @@ are for incomplete payments for an order and never for established contracts.
:http:statuscode:`200 OK`:
The merchant accepted the request, and passed it on to the exchange. The body is a
- a `merchant refund response <MerchantRefundResponse>`. Note that the exchange
+ a `abort response <AbortResponse>`. Note that the exchange
MAY still have encountered errors in processing. Those will then be part of
the body. Wallets MUST carefully consider errors for each of the coins as
returned by the exchange.
@@ -711,6 +792,10 @@ the contract. Refunds must be approved by the merchant's business logic.
// Amount that was refunded, including refund fee charged by the exchange
// to the customer.
refund_amount: Amount;
+
+ // Timestamp when the merchant approved the refund.
+ // Useful for grouping refunds.
+ execution_time: Timestamp;
}
.. ts:def:: MerchantCoinRefundSuccessStatus
@@ -745,108 +830,10 @@ the contract. Refunds must be approved by the merchant's business logic.
// Amount that was refunded, including refund fee charged by the exchange
// to the customer.
refund_amount: Amount;
- }
-
-
-Picking up tips
----------------
-
-Tips are a way for wallets to obtain e-cash from
-a website.
-
-.. http:get:: [/instances/$INSTANCE]/tips/$TIP_ID
- Handle request from wallet to provide details about a tip.
-
- This endpoint typically also supports requests with the "Accept" header
- requesting "text/html". In this case, an HTML response suitable for
- triggering the interaction with the wallet is returned. If the backend
- installation does not include the required HTML templates, a 406 status
- code is returned.
-
- **Response:**
-
- :http:statuscode:`200 OK`:
- A tip is being returned. The backend responds with a `TipInformation`.
- :http:statuscode:`404 Not found`:
- The tip identifier is unknown.
- :http:statuscode:`406 Not acceptable`:
- The merchant backend could not load the template required to generate a reply in the desired format. (Likely HTML templates were not properly installed.)
- :http:statuscode:`410 Gone`:
- A tip has been fully claimed. The JSON reply still contains the `TipInformation`.
-
- .. ts:def:: TipInformation
-
- interface TipInformation {
-
- // Exchange from which the tip will be withdrawn. Needed by the
- // wallet to determine denominations, fees, etc.
- exchange_url: string;
-
- // (Remaining) amount of the tip (including fees).
- tip_amount: Amount;
-
- // Timestamp indicating when the tip is set to expire (may be in the past).
- // Note that tips that have expired MAY also result in a 404 response.
- expiration: Timestamp;
- }
-
-
-.. http:post:: [/instances/$INSTANCE]/tips/$TIP_ID/pickup
-
- Handle request from wallet to pick up a tip.
-
- **Request:**
-
- The request body is a `TipPickupRequest` object.
-
- **Response:**
-
- :http:statuscode:`200 OK`:
- A tip is being returned. The backend responds with a `TipResponse`.
- :http:statuscode:`401 Unauthorized`:
- The tip amount requested exceeds the tip.
- :http:statuscode:`404 Not found`:
- The tip identifier is unknown.
- :http:statuscode:`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).
- :http:statuscode:`410 Gone`:
- The tip has expired.
-
- .. ts:def:: TipPickupRequest
-
- interface TipPickupRequest {
-
- // List of planchets 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[];
- }
-
- .. ts:def:: BlindSignature
-
- interface BlindSignature {
-
- // The (blind) RSA signature. Still needs to be unblinded.
- blind_sig: BlindedRsaSignature;
+ // Timestamp when the merchant approved the refund.
+ // Useful for grouping refunds.
+ execution_time: Timestamp;
}
@@ -855,7 +842,7 @@ Instance management
-------------------
Instances allow one merchant backend to be shared by multiple merchants.
-Every backend must have at least one instance, typcially the "default"
+Every backend must have at least one instance, typically the "default"
instance setup before it can be used to manage inventory or process payments.
@@ -886,13 +873,6 @@ Setting up instances
.. ts:def:: InstanceConfigurationMessage
interface InstanceConfigurationMessage {
- // The URI where the wallet will send coins. A merchant may have
- // multiple accounts, thus this is an array. Note that by
- // removing URIs from this list the respective account is set to
- // inactive and thus unavailable for new contracts, but preserved
- // in the database as existing offers and contracts may still refer
- // to it.
- payto_uris: string[];
// Name of the merchant instance to create (will become $INSTANCE).
// Must match the regex ``^[A-Za-z0-9][A-Za-z0-9_.@-]+$``.
@@ -901,6 +881,11 @@ Setting up instances
// Merchant name corresponding to this instance.
name: string;
+ // Type of the user (business or individual).
+ // Defaults to 'business'. Should become mandatory field
+ // in the future, left as optional for API compatibility for now.
+ user_type?: string;
+
// Merchant email for customer contact.
email?: string;
@@ -920,17 +905,10 @@ Setting up instances
// (to be put into contracts).
jurisdiction: Location;
- // Maximum wire fee this instance is willing to pay.
- // Can be overridden by the frontend on a per-order basis.
- default_max_wire_fee: Amount;
-
- // Default factor for wire fee amortization calculations.
- // Can be overridden by the frontend on a per-order basis.
- default_wire_fee_amortization: Integer;
-
- // Maximum deposit fee (sum over all coins) this instance is willing to pay.
- // Can be overridden by the frontend on a per-order basis.
- default_max_deposit_fee: Amount;
+ // Use STEFAN curves to determine default fees?
+ // If false, no fees are allowed by default.
+ // Can always be overridden by the frontend on a per-order basis.
+ use_stefan: boolean;
// If the frontend does NOT specify an execution date, how long should
// we tell the exchange to wait to aggregate transactions before
@@ -944,16 +922,15 @@ Setting up instances
}
-
.. http:post:: /management/instances/$INSTANCE/auth
.. http:post:: [/instances/$INSTANCE]/private/auth
- Update the authentication settings for an instance. POST operations against
- an instance are authenticated by checking that an authorization is provided
- that matches either the credential required by the instance being modified
- OR the ``default`` instance, depending on the access path used.
+ Update the authentication settings for an instance. POST operations against
+ an instance are authenticated by checking that an authorization is provided
+ that matches either the credential required by the instance being modified
+ OR the ``default`` instance, depending on the access path used.
- **Request** the request must be an `InstanceAuthConfigurationMessage`.
+ **Request** the request must be an `InstanceAuthConfigurationMessage`.
:http:statuscode:`204 No content`:
The backend has successfully created the instance.
@@ -971,7 +948,7 @@ Setting up instances
// See "token" for details.
method: "external" | "token";
- // For method "external", this field is mandatory.
+ // For method "token", this field is mandatory.
// The token MUST begin with the string "secret-token:".
// After the auth token has been set (with method "token"),
// the value must be provided in a "Authorization: Bearer $token"
@@ -980,6 +957,62 @@ Setting up instances
}
+.. http:post:: [/instances/$INSTANCE]/private/token
+
+ **Request:**
+
+ The request must be a `LoginTokenRequest`.
+
+ **Response:**
+
+ :http:statuscode:`200 Ok`:
+ The backend is returning the access token in a
+ `LoginTokenSuccessResponse`.
+
+ **Details:**
+
+ .. ts:def:: LoginTokenRequest
+
+ interface LoginTokenRequest {
+ // Scope of the token (which kinds of operations it will allow)
+ scope: "readonly" | "write";
+
+ // Server may impose its own upper bound
+ // on the token validity duration
+ duration?: RelativeTime;
+
+ // Can this token be refreshed?
+ // Defaults to false.
+ refreshable?: boolean;
+ }
+
+ .. ts:def:: LoginTokenSuccessResponse
+
+ interface LoginTokenSuccessResponse {
+ // The login token that can be used to access resources
+ // that are in scope for some time. Must be prefixed
+ // with "Bearer " when used in the "Authorization" HTTP header.
+ // Will already begin with the RFC 8959 prefix.
+ token: string;
+
+ // Scope of the token (which kinds of operations it will allow)
+ scope: "readonly" | "write";
+
+ // Server may impose its own upper bound
+ // on the token validity duration
+ expiration: Timestamp;
+
+ // Can this token be refreshed?
+ refreshable: boolean;
+ }
+
+.. http:delete:: [/instances/$INSTANCE]/private/token
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The access token used to authorize this request was revoked.
+
.. http:patch:: /management/instances/$INSTANCE
.. http:patch:: [/instances/$INSTANCE]/private
@@ -1005,15 +1038,15 @@ Setting up instances
.. ts:def:: InstanceReconfigurationMessage
interface InstanceReconfigurationMessage {
- // The URI where the wallet will send coins. A merchant may have
- // multiple accounts, thus this is an array. Note that removing
- // URIs from this list deactivates the specified accounts
- // (they will no longer be used for future contracts).
- payto_uris: string[];
// Merchant name corresponding to this instance.
name: string;
+ // Type of the user (business or individual).
+ // Defaults to 'business'. Should become mandatory field
+ // in the future, left as optional for API compatibility for now.
+ user_type?: string;
+
// Merchant email for customer contact.
email?: string;
@@ -1030,17 +1063,10 @@ Setting up instances
// (to be put into contracts).
jurisdiction: Location;
- // Maximum wire fee this instance is willing to pay.
- // Can be overridden by the frontend on a per-order basis.
- default_max_wire_fee: Amount;
-
- // Default factor for wire fee amortization calculations.
- // Can be overridden by the frontend on a per-order basis.
- default_wire_fee_amortization: Integer;
-
- // Maximum deposit fee (sum over all coins) this instance is willing to pay.
- // Can be overridden by the frontend on a per-order basis.
- default_max_deposit_fee: Amount;
+ // Use STEFAN curves to determine default fees?
+ // If false, no fees are allowed by default.
+ // Can always be overridden by the frontend on a per-order basis.
+ use_stefan: boolean;
// If the frontend does NOT specify an execution date, how long should
// we tell the exchange to wait to aggregate transactions before
@@ -1088,6 +1114,9 @@ Inspecting instances
// Merchant name corresponding to this instance.
name: string;
+ // Type of the user ("business" or "individual").
+ user_type: string;
+
// Merchant public website.
website?: string;
@@ -1127,13 +1156,13 @@ Inspecting instances
.. ts:def:: QueryInstancesResponse
interface QueryInstancesResponse {
- // The URI where the wallet will send coins. A merchant may have
- // multiple accounts, thus this is an array.
- accounts: MerchantAccount[];
// Merchant name corresponding to this instance.
name: string;
+ // Type of the user ("business" or "individual").
+ user_type: string;
+
// Merchant email for customer contact.
email?: string;
@@ -1153,17 +1182,10 @@ Inspecting instances
// (to be put into contracts).
jurisdiction: Location;
- // Maximum wire fee this instance is willing to pay.
- // Can be overridden by the frontend on a per-order basis.
- default_max_wire_fee: Amount;
-
- // Default factor for wire fee amortization calculations.
- // Can be overridden by the frontend on a per-order basis.
- default_wire_fee_amortization: Integer;
-
- // Maximum deposit fee (sum over all coins) this instance is willing to pay.
- // Can be overridden by the frontend on a per-order basis.
- default_max_deposit_fee: Amount;
+ // Use STEFAN curves to determine default fees?
+ // If false, no fees are allowed by default.
+ // Can always be overridden by the frontend on a per-order basis.
+ use_stefan: boolean;
// If the frontend does NOT specify an execution date, how long should
// we tell the exchange to wait to aggregate transactions before
@@ -1183,24 +1205,6 @@ Inspecting instances
}
- .. ts:def:: MerchantAccount
-
- interface MerchantAccount {
-
- // payto:// URI of the account.
- payto_uri: string;
-
- // Hash over the wire details (including over the salt).
- h_wire: HashCode;
-
- // Salt used to compute h_wire.
- salt: HashCode;
-
- // true if this account is active,
- // false if it is historic.
- active: boolean;
- }
-
Deleting instances
------------------
@@ -1274,15 +1278,25 @@ KYC status checks
request should be repeated.
The response will be an `AccountKycRedirects` object.
:http:statuscode:`204 No content`:
- All KYC operations queried have succeeded.
+ All KYC operations queried have succeeded. This may change in the
+ future, but there is no need to check again soon. It is suggested
+ to check again at a frequency of hours or days.
:http:statuscode:`502 Bad gateway`:
We failed to obtain a response from an exchange (about the KYC status).
The response will be an `AccountKycRedirects` object.
+ :http:statuscode:`503 Service unavailable`:
+ We do not know our KYC status as the exchange has not yet
+ returned the necessary details. This is not an actual failure:
+ this is expected to happen if for example a deposit was not yet aggregated
+ by the exchange and thus the exchange has not yet initiated
+ the KYC process. The client should simply try again later. It is
+ suggested to check again at a frequency of minutes to hours.
:http:statuscode:`504 Gateway Timeout`:
The merchant did not receive a confirmation from an exchange
within the specified time period. Used when long-polling for the
result.
- The response will be an `AccountKycRedirects` object.
+
+ **Details:**
.. ts:def:: AccountKycRedirects
@@ -1302,7 +1316,12 @@ KYC status checks
// URL that the user should open in a browser to
// proceed with the KYC process (as returned
// by the exchange's ``/kyc-check/`` endpoint).
- kyc_url: string;
+ // Optional, missing if the account is blocked
+ // due to AML and not due to KYC.
+ kyc_url?: string;
+
+ // AML status of the account.
+ aml_status: Integer;
// Base URL of the exchange this is about.
exchange_url: string;
@@ -1330,6 +1349,193 @@ KYC status checks
}
+
+-------------
+Bank Accounts
+-------------
+
+One or more bank accounts must be associated with an instance
+so that the instance can receive payments. Payments may be made
+into any of the active bank accounts of an instance.
+
+
+.. http:post:: [/instances/$INSTANCE]/private/accounts
+
+ This is used to add an account to an instance.
+
+ **Request:**
+
+ The request must have an `AccountAddDetails` body.
+
+ **Response:**
+
+ :http:statuscode:`200 Ok`:
+ Adding the account was successful, we return the salt selected by the backend and the resulting wire hash in an `AccountAddResponse`.
+ :http:statuscode:`404 Not found`:
+ The merchant instance is unknown or it is not in our data.
+ :http:statuscode:`409 Conflict`:
+ The provided information is inconsistent with the current state of the instance.
+ Usually this means we already have this account, but with conflicting credit facade information.
+ Inactive accounts can be reactivated using this method even if the
+ credit facade information differs from the previous state.
+
+ .. ts:def:: AccountAddDetails
+
+ interface AccountAddDetails {
+
+ // payto:// URI of the account.
+ payto_uri: string;
+
+ // URL from where the merchant can download information
+ // about incoming wire transfers to this account.
+ credit_facade_url?: string;
+
+ // Credentials to use when accessing the credit facade.
+ // Never returned on a GET (as this may be somewhat
+ // sensitive data). Can be set in POST
+ // or PATCH requests to update (or delete) credentials.
+ // To really delete credentials, set them to the type: "none".
+ credit_facade_credentials?: FacadeCredentials;
+
+ }
+
+ .. ts:def:: FacadeCredentials
+
+ type FacadeCredentials =
+ | NoFacadeCredentials
+ | BasicAuthFacadeCredentials;
+
+ .. ts:def:: NoFacadeCredentials
+
+ interface NoFacadeCredentials {
+ type: "none";
+ };
+
+ .. ts:def:: BasicAuthFacadeCredentials
+
+ interface BasicAuthFacadeCredentials {
+ type: "basic";
+
+ // Username to use to authenticate
+ username: string;
+
+ // Password to use to authenticate
+ password: string;
+ };
+
+ .. ts:def:: AccountAddResponse
+
+ interface AccountAddResponse {
+
+ // Hash over the wire details (including over the salt).
+ h_wire: HashCode;
+
+ // Salt used to compute h_wire.
+ salt: HashCode;
+
+ }
+
+
+.. http:patch:: [/instances/$INSTANCE]/private/accounts/$H_WIRE
+
+ This is used to update a bank account.
+
+ **Request:**
+
+ The request must be a `AccountPatchDetails`.
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The template has successfully modified.
+ :http:statuscode:`404 Not found`:
+ The template(ID) is unknown to the backend.
+
+ .. ts:def:: AccountPatchDetails
+
+ interface AccountPatchDetails {
+
+ // URL from where the merchant can download information
+ // about incoming wire transfers to this account.
+ credit_facade_url?: string;
+
+ // Credentials to use when accessing the credit facade.
+ // Never returned on a GET (as this may be somewhat
+ // sensitive data). Can be set in POST
+ // or PATCH requests to update (or delete) credentials.
+ // To really delete credentials, set them to the type: "none".
+ // If the argument is omitted, the old credentials
+ // are simply preserved.
+ credit_facade_credentials?: FacadeCredentials;
+ }
+
+
+.. http:get:: [/instances/$INSTANCE]/private/accounts
+
+ This is used to return the list of all the bank accounts
+ of an instance.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The backend has successfully returned all the accounts. Returns a `AccountsSummaryResponse`.
+ :http:statuscode:`404 Not found`:
+ The backend has does not know about the instance.
+
+ .. ts:def:: AccountsSummaryResponse
+
+ interface AccountsSummaryResponse {
+
+ // List of accounts that are known for the instance.
+ accounts: BankAccountEntry[];
+ }
+
+ .. ts:def:: BankAccountEntry
+
+ interface BankAccountEntry {
+
+ // payto:// URI of the account.
+ payto_uri: string;
+
+ // Hash over the wire details (including over the salt).
+ h_wire: HashCode;
+
+ // Salt used to compute h_wire.
+ salt: HashCode;
+
+ // URL from where the merchant can download information
+ // about incoming wire transfers to this account.
+ credit_facade_url?: string;
+
+ // true if this account is active,
+ // false if it is historic.
+ active: boolean;
+ }
+
+.. http:get:: [/instances/$INSTANCE]/private/accounts/$H_WIRE
+
+ This is used to obtain detailed information about a specific bank account.
+
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The backend has successfully returned the detailed information about a specific bank account.
+ Returns a `BankAccountEntry`.
+ :http:statuscode:`404 Not found`:
+ The bank account or instance is unknown to the backend.
+
+
+.. http:delete:: [/instances/$INSTANCE]/private/accounts/$H_WIRE
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The backend has successfully deactivated the account.
+ :http:statuscode:`404 Not found`:
+ The backend does not know the instance or the account.
+
+
--------------------
Inventory management
--------------------
@@ -1490,6 +1696,11 @@ Inspecting inventory
This is used to return the list of all items in the inventory.
+ **Request:**
+
+ :query limit: *Optional*. At most return the given number of results. Negative for descending by row ID, positive for ascending by row ID. Default is ``20``. Since protocol **v12**.
+ :query offset: *Optional*. Starting ``product_serial_id`` for an iteration. Since protocol **v12**.
+
**Response:**
:http:statuscode:`200 OK`:
@@ -1513,6 +1724,8 @@ Inspecting inventory
// Product identifier, as found in the product.
product_id: string;
+ // ``product_serial_id`` of the product in the database.
+ product_serial: integer;
}
@@ -1573,7 +1786,7 @@ Inspecting inventory
next_restock?: Timestamp;
// Minimum age buyer must have (in years).
- minimum_age: Integer;
+ minimum_age?: Integer;
}
@@ -1699,6 +1912,7 @@ Creating orders
(2) The merchant instance is unknown (including possibly the instance
being not configured for new orders).
(3) The wire method specified is not supported by the backend.
+ (4) An OTP device ID was specified and is unknown.
Details in the error code.
NOTE: currently the client has no good way to find out which product
@@ -1728,6 +1942,11 @@ Creating orders
// to select among the various (active) wire methods supported by the instance.
payment_target?: string;
+ // The session for which the payment is made (or replayed).
+ // Only set for session-based payments.
+ // Since protocol **v6**.
+ session_id?: string;
+
// Specifies that some products are to be included in the
// order from the inventory. For these inventory management
// is performed (so the products must be in stock) and
@@ -1742,7 +1961,7 @@ Creating orders
// be used in case different UUIDs were used for different
// products (i.e. in case the user started with multiple
// shopping sessions that were combined during checkout).
- lock_uuids: string[];
+ lock_uuids?: string[];
// Should a token for claiming the order be generated?
// False can make sense if the ORDER_ID is sufficiently
@@ -1750,6 +1969,9 @@ Creating orders
// if the backend auto-generates one). Default is 'true'.
create_token?: boolean;
+ // OTP device ID to associate with the order.
+ // This parameter is optional.
+ otp_id?: string;
}
.. ts:def:: Order
@@ -1768,11 +1990,14 @@ Creating orders
// Short summary of the order.
summary: string;
- // See documentation of fulfillment_url in ContractTerms.
+ // See documentation of fulfillment_url in `ContractTerms`.
// Either fulfillment_url or fulfillment_message must be specified.
+ // When creating an order, the fulfillment URL can
+ // contain ``${ORDER_ID}`` which will be substituted with the
+ // order ID of the newly created order.
fulfillment_url?: string;
- // See documentation of fulfillment_message in ContractTerms.
+ // See documentation of fulfillment_message in `ContractTerms`.
// Either fulfillment_url or fulfillment_message must be specified.
fulfillment_message?: string;
}
@@ -1848,10 +2073,14 @@ Inspecting orders
: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 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 delta: *Optional*. takes value of the form ``N (-N)``, so that at most ``N`` values strictly older (younger) than ``start`` and ``date_s`` are returned. Defaults to ``-20`` to return the last 20 entries (before ``start`` and/or ``date_s``).
+ :query delta: *Optional*. takes value of the form ``N (-N)``, so that at most ``N`` values strictly older (younger) than ``start`` and ``date_s`` are returned. Defaults to ``-20`` to return the last 20 entries (before ``start`` and/or ``date_s``). Deprecated in protocol **v12**. Use *limit* instead.
+ :query limit: *Optional*. At most return the given number of results. Negative for descending by row ID, positive for ascending by row ID. Default is ``20``. Since protocol **v12**.
:query date_s: *Optional.* Non-negative date in seconds after the UNIX Epoc, see ``delta`` for its interpretation. If not specified, we default to the oldest or most recent entry, depending on ``delta``.
- :query start: *Optional*. Row number threshold, see ``delta`` for its interpretation. Defaults to ``INT64_MAX``, namely the biggest row id possible in the database.
+ :query start: *Optional*. Row number threshold, see ``delta`` for its interpretation. Defaults to ``INT64_MAX``, namely the biggest row id possible in the database. Deprecated in protocol **v12**. Use *offset* instead.
+ :query offset: *Optional*. Starting ``product_serial_id`` for an iteration. Since protocol **v12**.
:query timeout_ms: *Optional*. Timeout in milliseconds 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.
+ :query session_id: *Optional*. Since protocol **v6**. Filters by session ID.
+ :query fulfillment_url: *Optional*. Since protocol **v6**. Filters by fulfillment URL.
**Response:**
@@ -1908,8 +2137,9 @@ Inspecting orders
**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 transfer: Deprecated in protocol **V6**. *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 milliseconds to wait for a payment if the answer would otherwise be negative (long polling).
+ :query allow_refunded_for_repurchase: *Optional*. Since protocol **v9** refunded orders are only returned under "already_paid_order_id" if this flag is set explicitly to "YES".
**Response:**
@@ -1972,6 +2202,7 @@ Inspecting orders
// Reports about trouble obtaining wire transfer details,
// empty array if no trouble were encountered.
+ // @deprecated in protocol **v6**.
wire_reports: TransactionWireReport[];
// The refund details for this order. One entry per
@@ -2246,35 +2477,12 @@ Informing the backend about incoming wire transfers
**Response:**
- :http:statuscode:`200 OK`:
- The wire transfer is known to the exchange, details about it follow in the body.
- The body of the response is a `MerchantTrackTransferResponse`.
- :http:statuscode:`202 Accepted`:
- 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 `ExchangeConflictDetails`.
- This is indicative of a malicious exchange that claims one thing, but did
- something else. (With respect to the HTTP specficiation, it is not
- precisely that we did not act upon the request, more that the usual
- action of filing the transaction as 'finished' does not apply. In
- the future, this is a case where the backend actually should report
- the bad behavior to the auditor -- and then hope for the auditor to
- resolve it. So in that respect, 202 is the right status code as more
- work remains to be done for a final resolution.)
+ :http:statuscode:`204 No content`:
+ The wire transfer is now confirmed at the merchant.
:http:statuscode:`404 Not found`:
The instance or account are unknown to the exchange.
:http:statuscode:`409 Conflict`:
- The wire transfer identifier is already known to us, but for a different amount,
- wire method or exchange, or the amount reported by the exchange differs from
- the amount reported by the merchant.
- :http:statuscode:`502 Bad gateway`:
- The exchange returned an error when we asked it about the ``GET /transfer`` status
- for this wire transfer. Details of the exchange error are returned.
- :http:statuscode:`504 Gateway timeout`:
- The merchant's interaction with the exchange took too long.
- The client might want to try again later.
+ The wire transfer identifier is already known to us, but for a different amount.
**Details:**
@@ -2294,168 +2502,6 @@ Informing the backend about incoming wire transfers
exchange_url: string;
}
- .. ts:def:: MerchantTrackTransferResponse
-
- interface MerchantTrackTransferResponse {
- // Total amount transferred.
- total: Amount;
-
- // Applicable wire fee that was charged.
- wire_fee: Amount;
-
- // Time of the execution of the wire transfer by the exchange, according to the exchange.
- execution_time: Timestamp;
-
- // Details about the deposits.
- deposits_sums: MerchantTrackTransferDetail[];
- }
-
- .. ts:def:: MerchantTrackTransferDetail
-
- interface MerchantTrackTransferDetail {
- // 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;
- }
-
-
- .. ts:def:: ExchangeConflictDetails
-
- type ExchangeConflictDetails = WireFeeConflictDetails |
- TrackTransferConflictDetails;
-
-
- .. ts:def:: WireFeeConflictDetails
-
- // Note: this is not the full 'proof' of misbehavior, as
- // the bogus message from the exchange with a signature
- // over the 'different' wire fee is missing.
- //
- // This information is NOT provided by the current implementation,
- // because this would be quite expensive to generate and is
- // hardly needed _here_. Once we add automated reports for
- // the Taler auditor, we need to generate this data anyway
- // and should probably return it here as well.
- interface WireFeeConflictDetails {
- // Numerical `error code <error-codes>`:
- code: "TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_BAD_WIRE_FEE";
-
- // Text describing the issue for humans.
- hint: string;
-
- // Wire fee (wrongly) charged by the exchange, breaking the
- // contract affirmed by the ``exchange_sig``.
- wire_fee: Amount;
-
- // Timestamp of the wire transfer.
- execution_time: Timestamp;
-
- // The expected wire fee (as signed by the exchange).
- expected_wire_fee: Amount;
-
- // Expected closing fee (needed to verify signature).
- expected_closing_fee: Amount;
-
- // Start date of the expected fee structure.
- start_date: Timestamp;
-
- // End date of the expected fee structure.
- end_date: Timestamp;
-
- // Signature of the exchange affirming the expected fee structure.
- master_sig: EddsaSignature;
-
- // Master public key of the exchange.
- master_pub: EddsaPublicKey;
- }
-
-
- .. ts:def:: TrackTransferConflictDetails
-
- interface TrackTransferConflictDetails {
- // Numerical `error code <error-codes>`.
- code: "TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_CONFLICTING_REPORTS";
-
- // Text describing the issue for humans.
- hint: string;
-
- // Offset in the ``exchange_transfer`` 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.
- //
- // This field is NOT provided by the current implementation,
- // because this would be quite expensive to generate and is
- // hardly needed _here_. Once we add automated reports for
- // the Taler auditor, we need to generate this data anyway
- // and should probably return it here as well.
- exchange_transfer?: TrackTransferResponse;
-
- // Public key of the exchange used to sign the response to
- // our deposit request.
- deposit_exchange_pub: EddsaPublicKey;
-
- // Signature of the exchange signing the (conflicting) response.
- // Signs over a ``struct TALER_DepositConfirmationPS``.
- deposit_exchange_sig: EddsaSignature;
-
- // Hash of the merchant's bank account the wire transfer went to.
- h_wire: HashCode;
-
- // Hash of the contract terms with the conflicting deposit.
- h_contract_terms: HashCode;
-
- // At what time the exchange received the deposit. Needed
- // to verify the ``exchange_sig``.
- deposit_timestamp: Timestamp;
-
- // At what time the refund possibility expired (needed to verify ``exchange_sig``).
- refund_deadline: Timestamp;
-
- // Public key of the coin for which we have conflicting information.
- coin_pub: EddsaPublicKey;
-
- // Amount the exchange counted the coin for in the transfer.
- amount_with_fee: Amount;
-
- // Expected value of the coin.
- coin_value: Amount;
-
- // Expected deposit fee of the coin.
- coin_fee: Amount;
-
- // Expected deposit fee of the coin.
- deposit_fee: Amount;
-
- }
-
- .. ts:def:: TrackTransferProof
-
- interface TrackTransferProof {
- // Signature from the exchange made with purpose
- // ``TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT``.
- 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.
- exchange_pub: EddsaSignature;
-
- // Hash of the wire details (identical for all deposits).
- // Needed to check the ``exchange_sig``
- h_wire: HashCode;
- }
-
Querying known wire transfers
-----------------------------
@@ -2557,413 +2603,951 @@ once we got a reply from the exchange.
The transfer cannot be deleted anymore.
---------------------
-Backend: Giving tips
---------------------
+-----------
+OTP Devices
+-----------
-Tips are a way for websites to give small amounts of e-cash to visitors (for
-example as a financial reward for providing information or watching
-advertizements). Tips are non-contractual: neither merchant nor consumer
-have any contractual information about the other party as a result of the
-tip.
+OTP devices can be used to allow offline merchants
+to validate that a customer made a payment.
-Create reserve
---------------
+.. http:post:: [/instances/$INSTANCE]/private/otp-devices
+
+ This is used to associate an OTP device with an instance.
+
+ **Request:**
+
+ The request must be a `OtpDeviceAddDetails`.
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The creation of the template is successful.
+ :http:statuscode:`404 Not found`:
+ The merchant instance is unknown or it is not in our data.
-Reserves are basically funds a merchant has provided
-to an exchange for a tipping campaign. Each reserve
-has a limited lifetime (say 2--4 weeks). Any funds
-not used to tip customers will automatically be wired
-back from the exchange to the originating account.
+ .. ts:def:: OtpDeviceAddDetails
-To begin tipping, a merchant must tell the backend
-to set up a reserve. The backend will return a
-reserve public key which must be used as the wire
-transfer subject when wiring the tipping campaign
-funds to the exchange.
+ interface OtpDeviceAddDetails {
-.. _tips:
-.. http:post:: [/instances/$INSTANCE]/private/reserves
+ // Device ID to use.
+ otp_device_id: string;
- Create a reserve for tipping.
+ // Human-readable description for the device.
+ otp_device_description: string;
- This request is **not** idempotent. However, while repeating
- it will create another reserve, that is generally pretty harmless
- (assuming only one of the reserves is filled with a wire transfer).
- Clients may want to eventually delete the unused reserves to
- avoid clutter.
+ // A key encoded with RFC 3548 Base32.
+ // IMPORTANT: This is not using the typical
+ // Taler base32-crockford encoding.
+ // Instead it uses the RFC 3548 encoding to
+ // be compatible with the TOTP standard.
+ otp_key: string;
+
+ // Algorithm for computing the POS confirmation.
+ // "NONE" or 0: No algorithm (no pos confirmation will be generated)
+ // "TOTP_WITHOUT_PRICE" or 1: Without amounts (typical OTP device)
+ // "TOTP_WITH_PRICE" or 2: With amounts (special-purpose OTP device)
+ // The "String" variants are supported @since protocol **v7**.
+ otp_algorithm: Integer | string;
+
+ // Counter for counter-based OTP devices.
+ otp_ctr?: Integer;
+ }
+
+
+.. http:patch:: [/instances/$INSTANCE]/private/otp-devices/$DEVICE_ID
+
+ This is used to update an OTP device. It is useful when we need to change information in the OTP device or when we have mistake some information.
**Request:**
- The request body is a `ReserveCreateRequest` object.
+ The request must be a `OtpDevicePatchDetails`.
**Response:**
- :http:statuscode:`200 OK`:
- The backend is waiting for the reserve to be established. The merchant
- must now perform the wire transfer indicated in the `ReserveCreateConfirmation`.
- :http:statuscode:`408 Request timeout`:
- The exchange did not respond on time.
+ :http:statuscode:`204 No content`:
+ The template has successfully modified.
+ :http:statuscode:`404 Not found`:
+ The template(ID) is unknown to the backend.
:http:statuscode:`409 Conflict`:
- The exchange does not support the requested wire method.
- :http:statuscode:`502 Bad gateway`:
- We could not obtain ``/wire`` details from the specified exchange base URL.
- :http:statuscode:`504 Gateway timeout`:
- The merchant's interaction with the exchange took too long.
- The client might want to try again later.
+ The provided information is inconsistent with the current state of the template. Changes made is the same with
+ another store.
- .. ts:def:: ReserveCreateRequest
+ .. ts:def:: OtpDevicePatchDetails
- interface ReserveCreateRequest {
- // Amount that the merchant promises to put into the reserve.
- initial_balance: Amount;
+ interface OtpDevicePatchDetails {
- // Exchange the merchant intends to use for tipping.
- exchange_url: string;
+ // Human-readable description for the device.
+ otp_device_description: string;
+
+ // A key encoded with RFC 3548 Base32.
+ // IMPORTANT: This is not using the typical
+ // Taler base32-crockford encoding.
+ // Instead it uses the RFC 3548 encoding to
+ // be compatible with the TOTP standard.
+ otp_key?: string;
+
+ // Algorithm for computing the POS confirmation.
+ otp_algorithm: Integer;
+
+ // Counter for counter-based OTP devices.
+ otp_ctr?: Integer;
+ }
+
+
+.. http:get:: [/instances/$INSTANCE]/private/otp-devices
+
+ This is used to return the list of all the OTP devices.
- // Desired wire method, for example "iban" or "x-taler-bank".
- wire_method: string;
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The backend has successfully returned all the templates. Returns a `OtpDeviceSummaryResponse`.
+ :http:statuscode:`404 Not found`:
+ The backend has does not know about the instance.
+
+ .. ts:def:: OtpDeviceSummaryResponse
+
+ interface OtpDeviceSummaryResponse {
+
+ // Array of devices that are present in our backend.
+ otp_devices: OtpDeviceEntry[];
}
- .. ts:def:: ReserveCreateConfirmation
+ .. ts:def:: OtpDeviceEntry
- interface ReserveCreateConfirmation {
- // Public key identifying the reserve.
- reserve_pub: EddsaPublicKey;
+ interface OtpDeviceEntry {
- // Wire account of the exchange where to transfer the funds.
- payto_uri: string;
+ // Device identifier.
+ otp_device_id: string;
+
+ // Human-readable description for the device.
+ device_description: string;
}
-.. http:get:: [/instances/$INSTANCE]/private/reserves
+.. http:get:: [/instances/$INSTANCE]/private/otp-devices/$DEVICE_ID
- Obtain list of reserves that have been created for tipping.
+ This is used to obtain detailed information about a specific OTP device.
- **Request:**
+ The client can provide additional inputs in the query to allow the backend
+ to compute and return a sample OTP code. Note that it is not an error if
+ the client provides query arguments that are not being used *or* that are
+ insufficient for the server to compute the ``otp_code``: If the client
+ provides inadequate query parameters, the ``otp_code`` is simply omitted
+ from the response.
+
+ **Query:**
- :query after: *Optional*. Only return reserves created after the given timestamp in milliseconds.
- :query active: *Optional*. Only return active/inactive reserves depending on the boolean given.
- :query failures: *Optional*. Only return reserves where we disagree with the exchange about the initial balance.
+ :query faketime=TIMESTAMP: *Optional*. Timestamp in seconds to use when calculating the current OTP code of the device. Since protocol **v10**.
+ :query price=AMOUNT: *Optional*. Price to use when calculating the current OTP code of the device. Since protocol **v10**.
**Response:**
:http:statuscode:`200 OK`:
- Returns a list of known tipping reserves.
- The body is a `TippingReserveStatus`.
+ The backend has successfully returned the detailed information about a specific OTP device.
+ Returns a `OtpDeviceDetails`.
+ :http:statuscode:`404 Not found`:
+ The OTP device or instance is unknown to the backend.
- .. ts:def:: TippingReserveStatus
+ .. ts:def:: OtpDeviceDetails
- interface TippingReserveStatus {
- // Array of all known reserves (possibly empty!).
- reserves: ReserveStatusEntry[];
- }
+ interface OtpDeviceDetails {
- .. ts:def:: ReserveStatusEntry
+ // Human-readable description for the device.
+ device_description: string;
- interface ReserveStatusEntry {
- // Public key of the reserve.
- reserve_pub: EddsaPublicKey;
+ // Algorithm for computing the POS confirmation.
+ //
+ // Currently, the following numbers are defined:
+ // 0: None
+ // 1: TOTP without price
+ // 2: TOTP with price
+ otp_algorithm: Integer;
+
+ // Counter for counter-based OTP devices.
+ otp_ctr?: Integer;
+
+ // Current time for time-based OTP devices.
+ // Will match the ``faketime`` argument of the
+ // query if one was present, otherwise the current
+ // time at the backend.
+ //
+ // Available since protocol **v10**.
+ otp_timestamp: Integer;
- // Timestamp when it was established.
- creation_time: Timestamp;
+ // Current OTP confirmation string of the device.
+ // Matches exactly the string that would be returned
+ // as part of a payment confirmation for the given
+ // amount and time (so may contain multiple OTP codes).
+ //
+ // If the ``otp_algorithm`` is time-based, the code is
+ // returned for the current time, or for the ``faketime``
+ // if a TIMESTAMP query argument was provided by the client.
+ //
+ // When using OTP with counters, the counter is **NOT**
+ // increased merely because this endpoint created
+ // an OTP code (this is a GET request, after all!).
+ //
+ // If the ``otp_algorithm`` requires an amount, the
+ // ``amount`` argument must be specified in the
+ // query, otherwise the ``otp_code`` is not
+ // generated.
+ //
+ // This field is *optional* in the response, as it is
+ // only provided if we could compute it based on the
+ // ``otp_algorithm`` and matching client query arguments.
+ //
+ // Available since protocol **v10**.
+ otp_code?: string;
- // Timestamp when it expires.
- expiration_time: Timestamp;
+ }
- // Initial amount as per reserve creation call.
- merchant_initial_amount: Amount;
+.. http:delete:: [/instances/$INSTANCE]/private/otp-devices/$DEVICE_ID
- // Initial amount as per exchange, 0 if exchange did
- // not confirm reserve creation yet.
- exchange_initial_amount: Amount;
+ **Response:**
- // Amount picked up so far.
- pickup_amount: Amount;
+ :http:statuscode:`204 No content`:
+ The backend has successfully deleted the OTP device.
+ :http:statuscode:`404 Not found`:
+ The backend does not know the instance or the OTP device.
+
+
+---------
+Templates
+---------
+
+The template is a backend feature that is used to allow wallets to create an
+order. This is useful in cases where a store does not have Internet
+connectivity or where a Web site wants to enable payments on a purely static
+Web page (for example to collect donations). In these cases, the GNU Taler
+wallet can be used to setup an order (and then of course pay for it).
+
+The template itself primarily provides order details that cannot be be changed
+by the customer when the wallet creates the order. The idea is that the user
+*may* be prompted to enter certain information, such as the amount to be paid,
+or the order summary (as a reminder to themselves or a message to the store),
+while the template provides all of the other contract details.
+
+The typical user-experience with templatates is that the user first scans a QR
+code or clicks on a taler://-URI which contains a ``pay-template`` (see `LSD
+0006 <https://lsd.gnunet.org/lsd0006/>`__). The URI specifies which values the
+user should supply, currently either nothing, the amount, the order summary or
+both. The URI may also specify defaults or partial defaults for those
+values. After the user has supplied those values, the wallet will use the
+public template API to create the order, then fetch the order details, and
+proceed as if it had been given the respective ``pay`` URI in the first place:
+show the full contract details and allow the user to make a payment. If the
+user chooses to aborts the payment, the wallet should give the user the
+opportunity to edit the values and create another order with different values.
+If the template does not include any values that the user is allowed to edit
+(so it is basically a fixed contract), the wallet should directly create the
+order and immediately proceed to the contract acceptance dialog.
+
+The business process for the templating API is also pretty simple. First, the
+private API is used to setup (or edit) the template, providing all of the
+contract terms that subsequently cannot be changed by the customer/wallet.
+This template data is then stored under the template ID which can be freely
+chosen. The SPA should also make it easy for the merchant to convert the
+template ID into a taler://-URI and/or QR code. Here, the merchant must
+additionally specify the defaults (if any) for the customer-editable values.
+Afterwards, the merchant can print out the QR code for display at the store,
+add a link to the taler://-URI and/or embed the respective QR-code image into
+their Web page.
+
+To receive a payment confirmation, the mechant may choose to configure a
+webhook in the merchant backend on the ``pay`` action, for example to send an
+SMS to their mobile phone. For points-of-sale without a mobile phone or
+Internet connectivity, the TBD mechanism can also be used to confirm payments.
+
+
+
+Adding templates
+----------------
- // Amount approved for tips that exceeds the pickup_amount.
- committed_amount: Amount;
+.. http:post:: [/instances/$INSTANCE]/private/templates
- // Is this reserve active (false if it was deleted but not purged)?
- active: boolean;
+ This is used to create a template.
+
+ **Request:**
+
+ The request must be a `TemplateAddDetails`.
+
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The creation of the template is successful.
+ :http:statuscode:`404 Not found`:
+ The merchant instance is unknown or it is not in our data.
+
+
+ .. ts:def:: TemplateAddDetails
+
+ interface TemplateAddDetails {
+
+ // Template ID to use.
+ template_id: string;
+
+ // Human-readable description for the template.
+ template_description: string;
+
+ // OTP device ID.
+ // This parameter is optional.
+ otp_id?: string;
+
+ // Fixed contract information for orders created from
+ // this template.
+ template_contract: TemplateContractDetails;
+
+ // Key-value pairs matching a subset of the
+ // fields from `template_contract` that are
+ // user-editable defaults for this template.
+ // Since protocol **v13**.
+ editable_defaults?: Object;
+
+ // Required currency for payments. Useful if no
+ // amount is specified in the `template_contract`
+ // but the user should be required to pay in a
+ // particular currency anyway. Merchant backends
+ // may reject requests if the `template_contract`
+ // or `editable_defaults` do
+ // specify an amount in a different currency.
+ // This parameter is optional.
+ // Since protocol **v13**.
+ required_currency?: string;
}
-Query funds remaining
----------------------
+ .. ts:def:: TemplateContractDetails
+
+ interface TemplateContractDetails {
+
+ // Human-readable summary for the template.
+ summary?: string;
+
+ // Required currency for payments to the template.
+ // The user may specify any amount, but it must be
+ // in this currency.
+ // This parameter is optional and should not be present
+ // if "amount" is given.
+ currency?: string;
+
+ // The price is imposed by the merchant and cannot be changed by the customer.
+ // This parameter is optional.
+ amount?: Amount;
+
+ // Minimum age buyer must have (in years). Default is 0.
+ minimum_age: Integer;
+
+ // The time the customer need to pay before his order will be deleted.
+ // It is deleted if the customer did not pay and if the duration is over.
+ pay_duration: RelativeTime;
+
+ }
-.. http:get:: [/instances/$INSTANCE]/private/reserves/$RESERVE_PUB
- Obtain information about a specific reserve that have been created for tipping.
+
+Editing templates
+-----------------
+
+
+.. http:patch:: [/instances/$INSTANCE]/private/templates/$TEMPLATE_ID
+
+ This is used to update a template. It is useful when we need to change information in the template or when we have mistake some information.
**Request:**
- :query tips: *Optional*. If set to "yes", returns also information about all of the tips created.
+ The request must be a `TemplatePatchDetails`.
**Response:**
- :http:statuscode:`200 OK`:
- Returns the `ReserveDetail`.
+ :http:statuscode:`204 No content`:
+ The template has successfully modified.
:http:statuscode:`404 Not found`:
- The tipping reserve is not known.
- :http:statuscode:`502 Bad gateway`:
- 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.
- :http:statuscode:`504 Gateway timeout`:
- The merchant's interaction with the exchange took too long.
- The client might want to try again later.
+ The template(ID) is unknown to the backend.
+ :http:statuscode:`409 Conflict`:
+ The provided information is inconsistent with the current state of the template. Changes made is the same with
+ another store.
- .. ts:def:: ReserveDetail
- interface ReserveDetail {
- // Timestamp when it was established.
- creation_time: Timestamp;
+ .. ts:def:: TemplatePatchDetails
- // Timestamp when it expires.
- expiration_time: Timestamp;
+ interface TemplatePatchDetails {
- // Initial amount as per reserve creation call.
- merchant_initial_amount: Amount;
+ // Human-readable description for the template.
+ template_description: string;
- // Initial amount as per exchange, 0 if exchange did
- // not confirm reserve creation yet.
- exchange_initial_amount: Amount;
+ // OTP device ID.
+ // This parameter is optional.
+ otp_id?: string;
- // Amount picked up so far.
- pickup_amount: Amount;
+ // Additional information in a separate template.
+ template_contract: TemplateContractDetails;
- // 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[];
- // Is this reserve active (false if it was deleted but not purged)?
- active: boolean;
- // URI to use to fill the reserve, can be NULL
- // if the reserve is inactive or was already filled
- payto_uri: string;
+Inspecting template
+-------------------
- // URL of the exchange hosting the reserve,
- // NULL if the reserve is inactive
- exchange_url: string;
+.. http:get:: [/instances/$INSTANCE]/private/templates
+
+ This is used to return the list of all the templates.
+
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The backend has successfully returned all the templates. Returns a `TemplateSummaryResponse`.
+ :http:statuscode:`404 Not found`:
+ The backend has does not know about the instance.
+
+ .. ts:def:: TemplateSummaryResponse
+
+ interface TemplateSummaryResponse {
+
+ // List of templates that are present in our backend.
+ templates: TemplateEntry[];
}
- .. ts:def:: TipStatusEntry
+ The `TemplateEntry` object describes a template. It has the following structure:
- interface TipStatusEntry {
+ .. ts:def:: TemplateEntry
- // Unique identifier for the tip.
- tip_id: HashCode;
+ interface TemplateEntry {
- // Total amount of the tip that can be withdrawn.
- total_amount: Amount;
+ // Template identifier, as found in the template.
+ template_id: string;
+
+ // Human-readable description for the template.
+ template_description: string;
- // Human-readable reason for why the tip was granted.
- reason: string;
}
+.. http:get:: [/instances/$INSTANCE]/private/templates/$TEMPLATE_ID
-Authorizing tips
-----------------
+ This is used to obtain detailed information about a specific template.
-.. http:post:: [/instances/$INSTANCE]/private/reserves/$RESERVE_PUB/authorize-tip
- Authorize creation of a tip from the given reserve.
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The backend has successfully returned the detailed information about a specific template.
+ Returns a `TemplateDetails`.
+ :http:statuscode:`404 Not found`:
+ The instance or template(ID) is unknown to the backend.
+
+
+ .. ts:def:: TemplateDetails
+
+ interface TemplateDetails {
+
+ // Human-readable description for the template.
+ template_description: string;
+
+ // OTP device ID.
+ // This parameter is optional.
+ otp_id?: string;
+
+ // Additional information in a separate template.
+ template_contract: TemplateContractDetails;
+ }
+
+
+
+Removing template
+-----------------
+
+.. http:delete:: [/instances/$INSTANCE]/private/templates/$TEMPLATE_ID
+
+ This is used to delete information about a template. If we no longer use it.
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The backend has successfully deleted the template.
+ :http:statuscode:`404 Not found`:
+ The backend does not know the instance or the template.
- **Request:**
- The request body is a `TipCreateRequest` object.
+
+Using template
+----------------
+
+.. http:get:: [/instances/$INSTANCE]/templates/$TEMPLATE_ID
+
+ This is used to obtain information about a specific template by wallets
+ before they ask the user to fill in details.
+ This endpoint is available since protocol **v11**.
**Response:**
:http:statuscode:`200 OK`:
- A tip has been created. The backend responds with a `TipCreateConfirmation`.
+ The backend has successfully returned the detailed information about a specific template.
+ Returns a `WalletTemplateDetails`.
:http:statuscode:`404 Not found`:
- The instance or the reserve is unknown to the backend.
- :http:statuscode:`412 Precondition failed`:
- The tip amount requested exceeds the available reserve balance for tipping.
+ The instance or template(ID) is unknown to the backend.
- .. ts:def:: TipCreateRequest
+ .. ts:def:: WalletTemplateDetails
- interface TipCreateRequest {
- // Amount that the customer should be tipped.
- amount: Amount;
+ interface WalletTemplateDetails {
- // Justification for giving the tip.
- justification: string;
+ // Hard-coded information about the contrac terms
+ // for this template.
+ template_contract: TemplateContractDetails;
+ }
+
+
+.. http:post:: [/instances/$INSTANCES]/templates/$TEMPLATE_ID
+
+ This using template can be modified by everyone and will be used to create order.
- // URL that the user should be directed to after tipping,
- // will be included in the tip_token.
- next_url: string;
- }
- .. ts:def:: TipCreateConfirmation
+ **Request:**
+
+ The request must be a `UsingTemplateDetails` and we accept JSON application and URL encoded.
+
- interface TipCreateConfirmation {
- // Unique tip identifier for the tip that was created.
- tip_id: HashCode;
+ **Response:**
+
+ The response is exactly the same type of response as when
+ creating an order using :ref:`POST /private/orders <post-order>`.
- // taler://tip URI for the tip.
- taler_tip_uri: string;
+ **Details:**
- // URL that will directly trigger processing
- // the tip when the browser is redirected to it.
- tip_status_url: string;
+ .. ts:def:: UsingTemplateDetails
- // When does the tip expire?
- tip_expiration: Timestamp;
+ interface UsingTemplateDetails {
+
+ // Summary of the template
+ summary?: string;
+
+ // The amount entered by the customer.
+ amount?: Amount;
}
-.. http:post:: [/instances/$INSTANCE]/private/tips
+--------
+Webhooks
+--------
+
+The webhook is a backend feature that is used to send a confirmation to the merchant. It can be send with a SMS, email or with another method. It will confirm that the
+customer paid the merchant. It will show the date and the price the customer paid.
+
+
- Authorize creation of a tip from the given reserve, except with
- automatic selection of a working reserve of the instance by the
- backend. Intentionally otherwise identical to the ``/authorize-tip``
- endpoint given above.
+Adding webhooks
+---------------
+
+.. http:post:: [/instances/$INSTANCES]/private/webhooks
+
+ This is used to create a webhook.
**Request:**
- The request body is a `TipCreateRequest` object.
+ The request must be a `WebhookAddDetails`.
**Response:**
- :http:statuscode:`200 OK`:
- A tip has been created. The backend responds with a `TipCreateConfirmation`.
+ :http:statuscode:`204 No content`:
+ The creation of the webhook is successsful.
+
:http:statuscode:`404 Not found`:
- The instance is unknown to the backend.
- :http:statuscode:`412 Precondition failed`:
- The tip amount requested exceeds the available reserve balance for tipping
- in all of the reserves of the instance.
+ The merchant instance is unknown or it not in our data.
+ .. ts:def:: WebhookAddDetails
-Deleting reserves
------------------
+ interface WebhookAddDetails {
+
+ // Webhook ID to use.
+ webhook_id: string;
+
+ // The event of the webhook: why the webhook is used.
+ event_type: string;
-.. http:delete:: [/instances/$INSTANCE]/private/reserves/$RESERVE_PUB
+ // URL of the webhook where the customer will be redirected.
+ url: string;
- 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.
+ // Method used by the webhook
+ http_method: string;
+
+ // Header template of the webhook
+ header_template?: string;
+
+ // Body template by the webhook
+ body_template?: string;
+
+ }
+
+
+Editing webhooks
+----------------
+
+.. http:patch:: [/instances/$INSTANCES]/private/webhooks/$WEBHOOK_ID
+
+ This is used to update a webhook.
**Request:**
- :query purge: *Optional*. If set to YES, the reserve and all information
- about tips it issued will be fully deleted.
- Otherwise only the private key would be deleted.
+ The request must be a `WebhookPatchDetails`.
**Response:**
:http:statuscode:`204 No content`:
- The backend has successfully deleted the reserve.
+ The webhook has successfully modified.
:http:statuscode:`404 Not found`:
- The backend does not know the instance or the reserve.
+ The webhook(ID) is unknown to the backend.
:http:statuscode:`409 Conflict`:
- The backend refuses to delete the reserve (committed tips awaiting pickup).
+ The provided information is inconsistent with the current state of the webhook. Changes made is the same with another store.
+ .. ts:def:: WebhookPatchDetails
-Checking tip status
--------------------
+ interface WebhookPatchDetails {
-.. http:get:: [/instances/$INSTANCE]/private/tips/$TIP_ID
+ // The event of the webhook: why the webhook is used.
+ event_type: string;
- Obtain information about a particular tip.
+ // URL of the webhook where the customer will be redirected.
+ url: string;
- **Request:**
+ // Method used by the webhook
+ http_method: string;
- :query pickups: If set to "yes", returns also information about all of the pickups.
+ // Header template of the webhook
+ header_template?: string;
+
+ // Body template by the webhook
+ body_template?: string;
+
+ }
+
+
+
+Inspecting webhook
+------------------
+
+.. http:get:: [/instances/$INSTANCES]/private/webhooks
+
+ This is used to return all the webhooks that are present in our backend.
**Response:**
:http:statuscode:`200 OK`:
- The tip is known. The backend responds with a `TipDetails` message.
+ The backend has successfully returned all the webhooks. Returns a `WebhookSummaryResponse`.
+
:http:statuscode:`404 Not found`:
- The tip is unknown to the backend.
+ The backend has does not know about the instance.
- .. ts:def:: TipDetails
+ .. ts:def:: WebhookSummaryResponse
- interface TipDetails {
- // Amount that we authorized for this tip.
- total_authorized: Amount;
+ interface WebhookSummaryResponse {
- // Amount that was picked up by the user already.
- total_picked_up: Amount;
+ // Return webhooks that are present in our backend.
+ webhooks: WebhookEntry[];
- // 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;
+ The `WebhookEntry` object describes a webhook. It has the following structure:
- // Reserve public key from which the tip is funded.
- reserve_pub: EddsaPublicKey;
+ .. ts:def:: WebhookEntry
+
+ interface WebhookEntry {
+
+ // Webhook identifier, as found in the webhook.
+ webhook_id: string;
+
+ // The event of the webhook: why the webhook is used.
+ event_type: string;
+
+ }
+
+
+.. http:get:: [/instances/$INSTANCES]/private/webhooks/$WEBHOOK_ID
+
+ This is used to obtain detailed information about apecific template.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The backend has successfully returned the detailed information about a specific webhook. Returns a `WebhookDetails`.
+
+ :http:statuscode:`404 Not found`:
+ The webhook(ID) is unknown to the backend.
- // Array showing the pickup operations of the wallet (possibly empty!).
- // Only present if asked for explicitly.
- pickups?: PickupDetail[];
- }
- .. ts:def:: PickupDetail
+ .. ts:def:: WebhookDetails
- interface PickupDetail {
- // Unique identifier for the pickup operation.
- pickup_id: HashCode;
+ interface WebhookDetails {
- // Number of planchets involved.
- num_planchets: Integer;
+ // The event of the webhook: why the webhook is used.
+ event_type: string;
+
+ // URL of the webhook where the customer will be redirected.
+ url: string;
+
+ // Method used by the webhook
+ http_method: string;
+
+ // Header template of the webhook
+ header_template?: string;
+
+ // Body template by the webhook
+ body_template?: string;
+
+ }
+
+
+Removing webhook
+-----------------
+
+.. http:delete:: [/instances/$INSTANCES]/private/webhooks/$WEBHOOK_ID
+
+ This is used to delete information about a webhook.
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The backend has successfully deleted the webhook.
+
+ :http:statuscode:`404 Not found`:
+ The webhook(ID) or the instance is unknown to the backend.
- // Total amount requested for this pickup_id.
- requested_amount: Amount;
+
+
+----------------------------------------
+Token Families: Subscriptions, Discounts
+----------------------------------------
+
+This API provides functionalities for the issuance, management, and revocation
+of token families. Tokens facilitate the implementation of subscriptions and
+discounts, engaging solely the merchant and the user. Each token family
+encapsulates details pertaining to its respective tokens, guiding the merchant's
+backend on the appropriate processing and handling.
+
+
+Creating token families
+-----------------------
+
+.. http:post:: [/instances/$INSTANCES]/private/tokenfamilies
+
+ This is used to create a token family.
+
+ **Request:**
+
+ The request must be a `TokenFamilyCreateRequest`.
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The token family was created successfully.
+
+ :http:statuscode:`404 Not found`:
+ The merchant backend is unaware of the instance.
+
+ .. ts:def:: TokenFamilyCreateRequest
+
+ interface TokenFamilyCreateRequest {
+
+ // Identifier for the token family consisting of unreserved characters
+ // according to RFC 3986.
+ slug: string;
+
+ // Human-readable name for the token family.
+ name: string;
+
+ // Human-readable description for the token family.
+ description: string;
+
+ // Optional map from IETF BCP 47 language tags to localized descriptions.
+ description_i18n?: { [lang_tag: string]: string };
+
+ // Start time of the token family's validity period.
+ // If not specified, merchant backend will use the current time.
+ valid_after?: Timestamp;
+
+ // End time of the token family's validity period.
+ valid_before: Timestamp;
+
+ // Validity duration of an issued token.
+ duration: RelativeTime;
+
+ // Kind of the token family.
+ kind: TokenFamilyKind;
+
+ }
+
+ .. ts:def:: TokenFamilyKind
+
+ enum TokenFamilyKind {
+ Discount = "discount",
+ Subscription = "subscription",
}
-.. http:get:: [/instances/$INSTANCE]/private/tips
+Updating token families
+-----------------------
+
+.. http:patch:: [/instances/$INSTANCES]/private/tokenfamilies/$TOKEN_FAMILY_SLUG
- Return the list of all tips.
+ This is used to update a token family.
**Request:**
- :query include_expired: *Optional*. If set to "yes", the result includes expired tips also. Otherwise, only active tips are returned.
+ The request must be a `TokenFamilyUpdateRequest`.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The token family was successsful updated. Returns a `TokenFamilyDetails`.
+
+ :http:statuscode:`404 Not found`:
+ The merchant backend is unaware of the token family or instance.
+
+ .. ts:def:: TokenFamilyUpdateRequest
- :query limit: *Optional*. At most return the given number of results. Negative for descending in database row id, positive for ascending in database row id.
+ interface TokenFamilyUpdateRequest {
- :query offset: *Optional*. Starting ``row_id`` for an iteration.
+ // Human-readable name for the token family.
+ name: string;
+
+ // Human-readable description for the token family.
+ description: string;
+
+ // Optional map from IETF BCP 47 language tags to localized descriptions.
+ description_i18n: { [lang_tag: string]: string };
+
+ // Start time of the token family's validity period.
+ valid_after: Timestamp;
+
+ // End time of the token family's validity period.
+ valid_before: Timestamp;
+
+ // Validity duration of an issued token.
+ duration: RelativeTime;
+
+ }
+
+
+
+Inspecting token families
+-------------------------
+
+.. http:get:: [/instances/$INSTANCES]/private/tokenfamilies
+
+ This is used to list all configured token families for an instance.
**Response:**
:http:statuscode:`200 OK`:
- The backend has successfully found the list of tips. The backend responds
- with a `TipsResponse`.
+ The merchant backend has successfully returned all token families.
+ Returns a `TokenFamiliesList`.
- .. ts:def:: TipsResponse
+ :http:statuscode:`404 Not found`:
+ The merchant backend is unaware of the instance.
- interface TipsResponse {
+ .. ts:def:: TokenFamiliesList
- // List of tips that are present in the backend.
- tips: Tip[];
- }
+ // TODO: Add pagination
- .. ts:def:: Tip
+ interface TokenFamiliesList {
- interface Tip {
+ // All configured token families of this instance.
+ token_families: TokenFamilySummary[];
- // ID of the tip in the backend database.
- row_id: number;
+ }
- // Unique identifier for the tip.
- tip_id: HashCode;
+ .. ts:def:: TokenFamilySummary
- // (Remaining) amount of the tip (including fees).
- tip_amount: Amount;
- }
+ interface TokenFamilySummary {
+ // Identifier for the token family consisting of unreserved characters
+ // according to RFC 3986.
+ slug: string;
+
+ // Human-readable name for the token family.
+ name: string;
+
+ // Start time of the token family's validity period.
+ valid_after: Timestamp;
+
+ // End time of the token family's validity period.
+ valid_before: Timestamp;
+
+ // Kind of the token family.
+ kind: TokenFamilyKind;
+ }
+
+
+.. http:get:: [/instances/$INSTANCES]/private/tokenfamilies/$TOKEN_FAMILY_SLUG
+
+ This is used to get detailed information about a specific token family.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The merchant backend has successfully returned the detailed information
+ about a specific token family. Returns a `TokenFamilyDetails`.
+
+ :http:statuscode:`404 Not found`:
+ The merchant backend is unaware of the token family or instance.
+
+
+ The `TokenFamilyDetails` object describes a configured token family.
+
+ .. ts:def:: TokenFamilyDetails
+
+ interface TokenFamilyDetails {
+
+ // Identifier for the token family consisting of unreserved characters
+ // according to RFC 3986.
+ slug: string;
+
+ // Human-readable name for the token family.
+ name: string;
+
+ // Human-readable description for the token family.
+ description: string;
+
+ // Optional map from IETF BCP 47 language tags to localized descriptions.
+ description_i18n?: { [lang_tag: string]: string };
+
+ // Start time of the token family's validity period.
+ valid_after: Timestamp;
+
+ // End time of the token family's validity period.
+ valid_before: Timestamp;
+
+ // Validity duration of an issued token.
+ duration: RelativeTime;
+
+ // Kind of the token family.
+ kind: TokenFamilyKind;
+
+ // How many tokens have been issued for this family.
+ issued: Integer;
+
+ // How many tokens have been redeemed for this family.
+ redeemed: Integer;
+
+ }
+
+
+
+Deleting token families
+-----------------------
+
+.. http:delete:: [/instances/$INSTANCES]/private/tokenfamilies/$TOKEN_FAMILY_SLUG
+
+ This is used to delete a token family. Issued tokens of this family will not
+ be spendable anymore.
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The backend has successfully deleted the token family.
+
+ :http:statuscode:`404 Not found`:
+ The merchant backend is unaware of the token family or instance.
@@ -2996,6 +3580,10 @@ The contract terms must have the following structure:
// encoded in it (such as a short product identifier and timestamp).
order_id: string;
+ // List of contract choices that the customer can select from.
+ // @since protocol **vSUBSCRIBE**
+ choices: ContractChoice[];
+
// Total price for the transaction.
// The exchange will subtract deposit fees from that amount
// before transferring it to the merchant.
@@ -3008,13 +3596,26 @@ The contract terms must have the following structure:
public_reorder_url?: string;
// URL that will show that the order was successful after
- // it has been paid for. Optional. When POSTing to the
- // merchant, the placeholder "${ORDER_ID}" will be
- // replaced with the actual order ID (useful if the
+ // it has been paid for. Optional, but either ``fulfillment_url``
+ // or ``fulfillment_message`` must be specified in every
+ // contract terms.
+ //
+ // If a non-unique fulfillment URL is used, a customer can only
+ // buy the order once and will be redirected to a previous purchase
+ // when trying to buy an order with the same fulfillment URL a second
+ // time. This is useful for digital goods that a customer only needs
+ // to buy once but should be able to repeatedly download.
+ //
+ // For orders where the customer is expected to be able to make
+ // repeated purchases (for equivalent goods), the fulfillment URL
+ // should be made unique for every order. The easiest way to do
+ // this is to include a unique order ID in the fulfillment URL.
+ //
+ // When POSTing to the merchant, the placeholder text "${ORDER_ID}"
+ // is be replaced with the actual order ID (useful if the
// order ID is generated server-side and needs to be
- // in the URL).
- // Note that this placeholder can only be used once.
- // Either fulfillment_url or fulfillment_message must be specified.
+ // in the URL). Note that this placeholder can only be used once.
+ // Front-ends may use other means to generate a unique fulfillment URL.
fulfillment_url?: string;
// Message shown to the customer after paying for the order.
@@ -3026,23 +3627,9 @@ The contract terms must have the following structure:
fulfillment_message_i18n?: { [lang_tag: string]: string };
// Maximum total deposit fee accepted by the merchant for this contract.
+ // Overrides defaults of the merchant instance.
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[];
@@ -3079,9 +3666,6 @@ The contract terms must have the following structure:
// 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[];
@@ -3118,13 +3702,105 @@ The contract terms must have the following structure:
// 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;
+ // Must really be an Object (not a string, integer, float or array).
+ extra?: Object;
+
+ // Minimum age the buyer must have (in years). Default is 0.
+ // This value is at least as large as the maximum over all
+ // mimimum age requirements of the products in this contract.
+ // It might also be set independent of any product, due to
+ // legal requirements.
+ minimum_age?: Integer;
+
}
The wallet must select an 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 `ContractChoice` object describes a possible choice within a contract. The
+choice is done by the customer and consists of in- and outputs. In the example
+of buying an article, the merchant could present the customer with the choice
+to use a valid subscription token or pay using a gift voucher. Available since
+protocol **vSUBSCRIBE**.
+
+.. ts:def:: ContractChoice
+
+ interface ContractChoice {
+ // Inputs that must be provided by the customer, if this choice is selected.
+ inputs: ContractInput[];
+
+ // Outputs provided by the merchant, if this choice is selected.
+ outputs: ContractOutput[];
+ }
+
+.. ts:def:: ContractInput
+
+ // For now, only token inputs are supported.
+ type ContractInput = ContractInputToken;
+
+.. ts:def:: ContractInputToken
+
+ interface ContractInputToken {
+
+ // Token input.
+ type: "token";
+
+ // How many units of the input are required. Defaults to 1 if not specified.
+ // Input with number == 0 are ignored by the merchant backend.
+ number?: Integer;
+
+ // Token family slug as configured in the merchant backend. Slug is unique
+ // across all configured tokens of a merchant.
+ token_family_slug: string;
+
+ // TODO: Is this only relevant for subscriptions or also for discounts?
+ // Start of the validity period of the subscription token. Based on this,
+ // the merchant will select the relevant signing key. Is null for discounts.
+ valid_after?: Timestamp;
+
+ }
+
+.. ts:def:: ContractOutput
+
+ type ContractOutput = ContractOutputToken | ContractOutputTaxReceipt;
+
+.. ts:def:: ContractOutputToken
+
+ interface ContractOutputToken {
+
+ // Token output.
+ type: "token";
+
+ // How many units of the output are yielded. Defaults to 1 if not specified.
+ // Output with number == 0 are ignored by the merchant backend.
+ number?: Integer;
+
+ // Token family slug as configured in the merchant backend. Slug is unique
+ // across all configured tokens of a merchant.
+ // TODO: Should we rename this to token_family_slug?
+ authority_label: string;
+
+ // TODO: Is this only relevant for subscriptions or also for discounts?
+ // Start of the validity period of the subscription token. Based on this,
+ // the merchant will select the relevant signing key. Is null for discounts.
+ valid_after?: Timestamp;
+
+ }
+
+.. ts:def:: ContractOutputTaxReceipt
+
+ interface ContractOutputTaxReceipt {
+
+ // Tax receipt output.
+ type: "tax-receipt";
+
+ // Base URL of the donation authority that will
+ // issue the tax receipt.
+ donau_url: string;
+
+ }
+
The `Product` object describes the product being purchased from the merchant.
It has the following structure:
@@ -3249,6 +3925,18 @@ It has the following structure:
// The exchange's base URL.
url: string;
+ // How much would the merchant like to use this exchange.
+ // The wallet should use a suitable exchange with high
+ // priority. The following priority values are used, but
+ // it should be noted that they are NOT in any way normative.
+ //
+ // 0: likely it will not work (recently seen with account
+ // restriction that would be bad for this merchant)
+ // 512: merchant does not know, might be down (merchant
+ // did not yet get /wire response).
+ // 1024: good choice (recently confirmed working)
+ priority: Integer;
+
// Master public key of the exchange.
master_pub: EddsaPublicKey;
}