26.1. Testing in the TOPS Staging Environment#

This document contains a detailled, step-by-step instructions for testing with the staging environment for the Taler Operations deployment.

The staging environment (on host rusty) is almost equivalent to the production environment. It is, however, not connected to a real bank account.

26.1.1. Prerequisites#

An administrator needs to enable your SSH key for testing in the staging environment.

26.1.2. Withdrawals: Basic Withdrawal#

  1. Scan the following QR code to start a withdrawal from the stage exchange:

    ../_images/qr-withdraw-tops-stage.png

    taler://withdraw-exchange/exchange.stage.taler-ops.ch/

  2. Confirm the withdrawal in the wallet.

  3. Run the following command to fake a wire transfer, using a randomly generated creditor IBAN. The $AMOUNT and $SUBJECT should be taken from the wire transfer instructions in the wallet UI.

    ssh -T devtesting@rusty.taler-ops.ch "fake-incoming --amount $AMOUNT --subject '$SUBJECT'"
    
  4. Check that the withdrawal succeeded in the wallet.

26.1.3. Deposits: Basic Deposit#

  1. Do a basic withdrawal.

  2. Initiate a deposit in the wallet UI, using the receiver account known to the wallet from the withdrawal.

  3. The deposit transaction should now ask for KYC information. Fill out the corresponding form(s).

  4. The deposit transaction should finish.

26.1.4. Deposits: Deposit with KYC Auth#

  1. Do a basic withdrawal.

  2. Generate an IBAN via:

    ssh -T devtesting@rusty.taler-ops.ch geniban
    
  3. Initiate a deposit in the wallet UI to the randomly generated IBAN.

  4. The deposit transaction should now ask for a KYC auth transfer. Satisfy the auth transfer by running:

    ssh -T devtesting@rusty.taler-ops.ch "fake-incoming --amount $AMOUNT --subject '$SUBJECT' --debit-payto '$DEBIT_PAYTO'"
    

    with the information shown in the wallet UI, where the $DEBIT_PAYTO must contain the IBAN generated in step (2).

  5. The deposit transaction should now ask for KYC information. Fill out the corresponding form(s).

  6. The deposit transaction should finish.

26.1.5. Merchant: End-to-end onboarding (staging)#

Stage-specific glue for the merchant path already described elsewhere: Merchant Backend Operator Manual (SPA and HTTP API) and Merchant: Onboarding in Taler Operations Deployment (bank account → KYC auth → accept exchange terms of service → accept payments). Authentication is documented under Authentication. Wire faking reuses geniban / fake-incoming from Deposits: Deposit with KYC Auth above.

Two variants: SPA (mostly manual) below, then API (curl / scripted self-provision, login tokens, and orders).

This page is written for TOPS stage ($BASE below). Deployments like regional currencies — overview at https://regio.taler.net/, setup via taler-deployment.git regional-currency/ (see the automated setup and custom setup manuals) — can differ. Look at the merchant /config: if mandatory_tan_channels is empty, MFA for self-provisioning is effectively off and POST /instances often returns 204 (instance exists immediately, no /challenge/… round-trip) — typical for many regio setups. Creating orders still needs at least one active bank account (otherwise the backend rejects with a missing-account error). Wire faking via rusty / taler-devtesting is TOPS-stage specific (not the usual regional-bank funding path).

Variables used below (set your own values):

BASE=https://stage.my.taler-ops.ch
INSTANCE=my-shop          # instance id (slug)
INSTANCE_PASSWORD=…       # password chosen at signup
EMAIL=you@example.com     # inbox that can receive stage TAN mail
PHONE=+41 7x xxx xx xx    # real CH mobile; must match phone_regex
                          # from $BASE/config *and* be accepted by
                          # the SMS helper (dummy numbers fail with
                          # TALER_EC 2132 on /challenge/$ID)
IBAN=…                    # from geniban
DEBIT_PAYTO=payto://iban/$IBAN
TOKEN=…                   # access_token from /private/token
ORDER_ID=…                # from PostOrderResponse

$BASE/ redirects to $BASE/webui/. Use the stage exchange for payments. Check have_self_provisioning and mandatory_tan_channels on $BASE/config.

26.1.5.1. Shared bank / KYC steps#

  1. Generate an IBAN for the merchant bank account:

    IBAN=$(ssh -T devtesting@rusty.taler-ops.ch geniban)
    DEBIT_PAYTO="payto://iban/$IBAN"
    
  2. Register that IBAN on the instance (Instance bank account setup) — in the portal under Bank account, or via API after you have a $TOKEN:

    curl -sS \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d "{\"payto_uri\":\"$DEBIT_PAYTO\"}" \
      "$BASE/instances/$INSTANCE/private/accounts"
    

    Skip revenue-API credentials unless an administrator provided stage ones.

  3. When the exchange requires the KYC auth transfer (Merchant: Onboarding in Taler Operations Deployment), satisfy it like Deposits: Deposit with KYC Auth. Take $AMOUNT and $SUBJECT from the KYC UI:

    ssh -T devtesting@rusty.taler-ops.ch \
      "fake-incoming --amount $AMOUNT --subject '$SUBJECT' --debit-payto '$DEBIT_PAYTO'"
    
  4. If the deposit limit is still zero, accept the TOPS exchange terms of service on the exchange KYC page (Merchant: Onboarding in Taler Operations Deployment). For stuck account validation, see Troubleshooting in the TOPS Prod Environment.

  5. Fund a wallet via Withdrawals: Basic Withdrawal above, then pay the order (Paying an order in the merchant manual).

26.1.5.2. SPA (login with password)#

Normal interactive path (Instance setup, Self-provisioning, Manually creating an order using the SPA).

  1. Open $BASE/webui/. Sign up or log in with $INSTANCE and $INSTANCE_PASSWORD. Confirm e-mail and SMS TAN when asked (both channels are mandatory on this backend).

  2. Complete Shared bank / KYC steps in the portal (Bank account, KYC links).

  3. Orders → create (summary + amount). Keep the payment QR / pay link from the order status page.

  4. Pay from the wallet (shared step 5) and confirm paid status in the SPA.

26.1.5.3. API (self-provision → password → login token → Bearer)#

Same backend via HTTP (POST /instances, Authentication, POST [/instances/$INSTANCE]/private/token, POST [/instances/$INSTANCE]/private/orders). Private endpoints need Authorization: Bearer secret-token:… (RFC 8959).

On this stage backend, expect the MFA path below (mandatory_tan_channels is email and sms). Where that list is empty, skip to step 3 after a 204 create.

  1. Self-provision (POST /instances). Example body (InstanceConfigurationRequest; auth.method = token):

    curl -sS -H 'Content-Type: application/json' \
      -d "{
        \"id\": \"$INSTANCE\",
        \"name\": \"Example Shop\",
        \"email\": \"$EMAIL\",
        \"phone_number\": \"$PHONE\",
        \"auth\": {\"method\": \"token\", \"password\": \"$INSTANCE_PASSWORD\"},
        \"address\": {\"country\": \"CH\"},
        \"jurisdiction\": {\"country\": \"CH\"},
        \"use_stefan\": true
      }" \
      "$BASE/instances"
    

    Typical response before MFA: HTTP 202 ChallengeResponse with combi_and: true and one challenge per TAN channel, e.g.:

    {
      "combi_and": true,
      "challenges": [
        {"tan_channel": "email", "tan_info": "…", "challenge_id": "$CHALLENGE_EMAIL"},
        {"tan_channel": "sms",   "tan_info": "…", "challenge_id": "$CHALLENGE_SMS"}
      ]
    }
    

    Until both TANs are solved, $INSTANCE does not exist (/instances/$INSTANCE/private/… → 404).

  2. Request and confirm each TAN on the unprefixed challenge URLs (instance path segment optional; before create, only this form works — see Solving challenges in the merchant API):

    curl -sS -H 'Content-Type: application/json' -d '{}' \
      "$BASE/challenge/$CHALLENGE_ID"
    curl -sS -H 'Content-Type: application/json' \
      -d "{\"tan\":\"$TAN\"}" \
      "$BASE/challenge/$CHALLENGE_ID/confirm"
    

    Repeat for every challenge_id. A successful request returns 200; a rejected SMS/e-mail address returns 400 with code 2132 (fix $PHONE / $EMAIL). Wrong $TAN on confirm returns 409.

    Retry the same POST $BASE/instances body with:

    -H "Taler-Challenge-Ids: $CHALLENGE_EMAIL,$CHALLENGE_SMS"
    

    Expect 200 / 204 when the instance is created. With unsolved challenges the retry stays at 202.

  3. Password → login token (Basic: user $INSTANCE, password $INSTANCE_PASSWORD; LoginTokenRequest):

    curl -sS -u "$INSTANCE:$INSTANCE_PASSWORD" \
      -H 'Content-Type: application/json' \
      -d '{"scope":"order-simple"}' \
      "$BASE/instances/$INSTANCE/private/token"
    

    From LoginTokenSuccessResponse, set TOKEN to access_token (already includes the secret-token: prefix). Scopes such as spa or all work the same way if you need broader rights.

  4. Create an order with the Bearer token:

    curl -sS \
      -H "Authorization: Bearer $TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"order":{"summary":"stage test","amount":"CHF:1"}}' \
      "$BASE/instances/$INSTANCE/private/orders"
    
  5. Read order_id from PostOrderResponse. Fetch taler_pay_uri via GET [/instances/$INSTANCE]/private/orders/$ORDER_ID (or the public order status URL). Finish Shared bank / KYC steps if needed, then pay from the wallet.

Note

Prefer login tokens from /private/token. Long-lived Authorization: Bearer secret-token:$INSTANCE_PASSWORD is the deprecated token method and will be phased out.