merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit 5f8720069edd14407656762dd995ad709152ce72
parent 9f427ed5ee6e3fd33031f49f2d62d97e90754ded
Author: Christian Grothoff <christian@grothoff.org>
Date:   Fri,  2 Jan 2026 19:04:03 +0100

get first money pot test to pass

Diffstat:
Msrc/backend/taler-merchant-httpd_dispatcher.c | 32++++++++++++++++++++++++++++++++
Msrc/backend/taler-merchant-httpd_post-orders-ID-pay.c | 115++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Msrc/backend/taler-merchant-httpd_private-post-orders.c | 5+++++
Msrc/backenddb/pg_increment_money_pots.sql | 8++++----
Msrc/backenddb/pg_insert_money_pot.c | 5++---
Msrc/testing/Makefile.am | 1+
Asrc/testing/test_merchant_statistics.sh | 378+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 498 insertions(+), 46 deletions(-)

diff --git a/src/backend/taler-merchant-httpd_dispatcher.c b/src/backend/taler-merchant-httpd_dispatcher.c @@ -701,6 +701,38 @@ determine_handler_group (const char **urlp, in the code... */ .max_upload = 1024 * 1024 * 8 }, + + /* POST /pots: */ + { + .url_prefix = "/pots", + .method = MHD_HTTP_METHOD_POST, + .permission = "pots-write", + .handler = &TMH_private_post_pots, + }, + /* GET /pots: */ + { + .url_prefix = "/pots", + .permission = "pots-read", + .method = MHD_HTTP_METHOD_GET, + .handler = &TMH_private_get_pots + }, + /* DELETE /pots/$ID: */ + { + .url_prefix = "/pots/", + .method = MHD_HTTP_METHOD_DELETE, + .permission = "pots-write", + .have_id_segment = true, + .handler = &TMH_private_delete_pot + }, + /* PATCH /pots/$ID: */ + { + .url_prefix = "/pots/", + .method = MHD_HTTP_METHOD_PATCH, + .permission = "pots-write", + .have_id_segment = true, + .handler = &TMH_private_patch_pot, + }, + /* GET /webhooks: */ { .url_prefix = "/webhooks", diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c @@ -719,6 +719,11 @@ struct PayContext */ struct TALER_Amount *increments; + /** + * True if the money pots have already been computed. + */ + bool pots_computed; + } compute_money_pots; /** @@ -2292,6 +2297,16 @@ phase_compute_money_pots (struct PayContext *pc) return; } + if (pc->compute_money_pots.pots_computed) + { + pc->phase++; + return; + } + /* reset, in case this phase is run a 2nd time */ + GNUNET_free (pc->compute_money_pots.pots); + GNUNET_free (pc->compute_money_pots.increments); + pc->compute_money_pots.num_pots = 0; + TALER_amount_set_zero (pc->parse_pay.dc[0].cdd.amount.currency, &assigned); GNUNET_assert (NULL != contract); @@ -2326,6 +2341,10 @@ phase_compute_money_pots (struct PayContext *pc) TALER_amount_add (&assigned, &assigned, price)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Contributing to product money pot %llu increment of %s\n", + (unsigned long long) product->product_money_pot, + TALER_amount2s (price)); increment_pot (pc, product->product_money_pot, price); @@ -2338,6 +2357,9 @@ phase_compute_money_pots (struct PayContext *pc) is below that of the sum of the products. */ struct TALER_Amount left; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Order brutto is %s\n", + TALER_amount2s (&pc->validate_tokens.brutto)); if (0 > TALER_amount_subtract (&left, &pc->validate_tokens.brutto, @@ -2353,10 +2375,17 @@ phase_compute_money_pots (struct PayContext *pc) if ( (! TALER_amount_is_zero (&left)) && (0 != contract->default_money_pot) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Computing money pot %llu increment as %s\n", + (unsigned long long) contract->default_money_pot, + TALER_amount2s (&left)); increment_pot (pc, contract->default_money_pot, &left); + } } + pc->compute_money_pots.pots_computed = true; pc->phase++; } @@ -2801,40 +2830,6 @@ phase_execute_pay_transaction (struct PayContext *pc) return; } - if (0 < pc->compute_money_pots.num_pots) - { - enum GNUNET_DB_QueryStatus qs; - - qs = TMH_db->increment_money_pots (TMH_db->cls, - instance_id, - pc->compute_money_pots.num_pots, - pc->compute_money_pots.pots, - pc->compute_money_pots.increments); - switch (qs) - { - case GNUNET_DB_STATUS_SOFT_ERROR: - TMH_db->rollback (TMH_db->cls); - return; /* do it again */ - case GNUNET_DB_STATUS_HARD_ERROR: - /* Always report on hard error as well to enable diagnostics */ - TMH_db->rollback (TMH_db->cls); - pay_end (pc, - TALER_MHD_reply_with_error (pc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "increment_money_pots")); - return; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - /* strange */ - GNUNET_break (0); - break; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - /* Good, proceed! */ - break; - } - - } - for (size_t i = 0; i<pc->parse_pay.tokens_cnt; i++) { struct TokenUseConfirmation *tuc = &pc->parse_pay.tokens[i]; @@ -2906,11 +2901,11 @@ phase_execute_pay_transaction (struct PayContext *pc) TMH_db->rollback (TMH_db->cls); GNUNET_break_op (0); pay_end (pc, - TALER_MHD_reply_with_error (pc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH, - pc->validate_tokens.brutto.currency)) - ; + TALER_MHD_reply_with_error ( + pc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH, + pc->validate_tokens.brutto.currency)); return; } } @@ -2996,6 +2991,48 @@ phase_execute_pay_transaction (struct PayContext *pc) "mark contract paid")); return; } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Marked contract paid returned %d\n", + (int) qs); + + if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) && + (0 < pc->compute_money_pots.num_pots) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Incrementing %u money pots by %s\n", + pc->compute_money_pots.num_pots, + TALER_amount2s (&pc->compute_money_pots.increments[0])); + qs = TMH_db->increment_money_pots (TMH_db->cls, + instance_id, + pc->compute_money_pots.num_pots, + pc->compute_money_pots.pots, + pc->compute_money_pots.increments); + switch (qs) + { + case GNUNET_DB_STATUS_SOFT_ERROR: + TMH_db->rollback (TMH_db->cls); + return; /* do it again */ + case GNUNET_DB_STATUS_HARD_ERROR: + /* Always report on hard error as well to enable diagnostics */ + TMH_db->rollback (TMH_db->cls); + pay_end (pc, + TALER_MHD_reply_with_error (pc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "increment_money_pots")); + return; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + /* strange */ + GNUNET_break (0); + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + /* Good, proceed! */ + break; + } + + } + + } diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c @@ -3362,6 +3362,9 @@ phase_merge_inventory (struct OrderContext *oc) e++; pots_off = e; } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Found %u unique money pots in order\n", + (unsigned int) pots_off); /* check if all money pots exist; note that we do NOT treat the inventory products to this check, as (1) the foreign key @@ -3390,6 +3393,8 @@ phase_merge_inventory (struct OrderContext *oc) return; case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: /* great, good case! */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "All money pots exist\n"); break; case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: { diff --git a/src/backenddb/pg_increment_money_pots.sql b/src/backenddb/pg_increment_money_pots.sql @@ -47,13 +47,13 @@ END IF; out_not_found = FALSE; -IF COALESCE(array_length(ina_money_pots_ids, 1), 0) != - COALESCE(array_length(ina_increments, 1), 0) +IF ( COALESCE(array_length(ina_money_pots_ids, 1), 0) != + COALESCE(array_length(ina_increments, 1), 0) ) THEN RAISE EXCEPTION 'Array lengths must match'; END IF; -FOR i IN 1..array_length(ina_money_pots_ids, 1) +FOR i IN 1..COALESCE(array_length(ina_money_pots_ids, 1),0) LOOP ini_current_pot_id = ina_money_pots_ids[i]; ini_current_increment = ina_increments[i]; @@ -75,7 +75,7 @@ FOR i IN 1..array_length(ina_money_pots_ids, 1) -- Check if currency exists in pot_totals and update currency_found = FALSE; - FOR j IN 1..array_length(my_totals, 1) + FOR j IN 1..COALESCE(array_length(my_totals, 1), 0) LOOP IF (my_totals[j]).currency = (ini_current_increment).currency THEN diff --git a/src/backenddb/pg_insert_money_pot.c b/src/backenddb/pg_insert_money_pot.c @@ -53,9 +53,8 @@ TMH_PG_insert_money_pot ( "INSERT INTO merchant_money_pots" "(merchant_serial" ",money_pot_name" - ",money_pot_description" - ",pot_total)" - " SELECT merchant_serial, $2, $3, $4" + ",money_pot_description)" + " SELECT merchant_serial, $2, $3" " FROM merchant_instances" " WHERE merchant_id=$1" " ON CONFLICT DO NOTHING" diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am @@ -16,6 +16,7 @@ check_SCRIPTS = \ test_merchant_order_creation.sh \ test_merchant_kyc.sh \ test_merchant_order_autocleanup.sh \ + test_merchant_statistics.sh \ test_merchant_wirewatch.sh \ test-merchant-walletharness.sh \ test_merchant_transfer_tracking.sh diff --git a/src/testing/test_merchant_statistics.sh b/src/testing/test_merchant_statistics.sh @@ -0,0 +1,378 @@ +#!/bin/bash +# This file is in the public domain. + +set -eu + +function clean_wallet() { + rm -f "${WALLET_DB}" + exit_cleanup +} + + +# Replace with 0 for nexus... +USE_FAKEBANK=1 +if [ 1 = "$USE_FAKEBANK" ] +then + ACCOUNT="exchange-account-2" + BANK_FLAGS="-f -d x-taler-bank -u $ACCOUNT" + BANK_URL="http://localhost:8082/" +else + ACCOUNT="exchange-account-1" + BANK_FLAGS="-ns -d iban -u $ACCOUNT" + BANK_URL="http://localhost:18082/" + echo -n "Testing for libeufin-bank" + libeufin-bank --help >/dev/null </dev/null || exit_skip " MISSING" + echo " FOUND" + +fi + +. setup.sh + +echo -n "Testing for taler-harness" +taler-harness --help >/dev/null </dev/null || exit_skip " MISSING" +echo " FOUND" + +# Launch exchange, merchant and bank. +setup -c "test_template.conf" \ + -r "merchant-exchange-default" \ + -em \ + $BANK_FLAGS +LAST_RESPONSE=$(mktemp -p "${TMPDIR:-/tmp}" test_response.conf-XXXXXX) +CONF="test_template.conf.edited" +WALLET_DB=$(mktemp -p "${TMPDIR:-/tmp}" test_wallet.json-XXXXXX) +EXCHANGE_URL="http://localhost:8081/" + +# Install cleanup handler (except for kill -9) +trap clean_wallet EXIT + +echo -n "First prepare wallet with coins ..." +rm -f "$WALLET_DB" +taler-wallet-cli \ + --no-throttle \ + --wallet-db="$WALLET_DB" \ + api \ + --expect-success 'withdrawTestBalance' \ + "$(jq -n ' + { + amount: "TESTKUDOS:99", + corebankApiBaseUrl: $BANK_URL, + exchangeBaseUrl: $EXCHANGE_URL + }' \ + --arg BANK_URL "${BANK_URL}" \ + --arg EXCHANGE_URL "$EXCHANGE_URL" + )" 2>wallet-withdraw-1.err >wallet-withdraw-1.out +echo -n "." +# FIXME-MS: add logic to have nexus check immediately here. +# sleep 10 +echo -n "." +# NOTE: once libeufin can do long-polling, we should +# be able to reduce the delay here and run wirewatch +# always in the background via setup +taler-exchange-wirewatch \ + -a "$ACCOUNT" \ + -L "INFO" \ + -c "$CONF" \ + -t &> taler-exchange-wirewatch.out +echo -n "." +taler-wallet-cli \ + --wallet-db="$WALLET_DB" \ + run-until-done \ + 2>wallet-withdraw-finish-1.err \ + >wallet-withdraw-finish-1.out +echo " OK" + +CURRENCY_COUNT=$(taler-wallet-cli --wallet-db="$WALLET_DB" balance | jq '.balances|length') +if [ "$CURRENCY_COUNT" = "0" ] +then + exit_fail "Expected least one currency, withdrawal failed. check log." +fi + +# +# CREATE INSTANCE FOR TESTING +# + + +echo -n "Configuring merchant instance ..." + +STATUS=$(curl -H "Content-Type: application/json" -X POST \ + -H 'Authorization: Bearer secret-token:super_secret' \ + http://localhost:9966/management/instances \ + -d '{"auth":{"method":"external"},"id":"admin","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 50000000000},"default_pay_delay":{"d_us": 60000000000}}' \ + -w "%{http_code}" -s -o /dev/null) + +if [ "$STATUS" != "204" ] +then + exit_fail "Expected '204 No content' response. Got instead $STATUS" +fi +echo "Ok" + +echo -n "Configuring merchant account ..." + +if [ 1 = "$USE_FAKEBANK" ] +then + FORTYTHREE="payto://x-taler-bank/localhost/fortythree?receiver-name=fortythree" +else + FORTYTHREE=$(get_payto_uri fortythree x) +fi +# create with 2 bank account addresses +STATUS=$(curl -H "Content-Type: application/json" -X POST \ + -H 'Authorization: Bearer secret-token:super_secret' \ + http://localhost:9966/private/accounts \ + -d '{"payto_uri":"'"$FORTYTHREE"'"}' \ + -w "%{http_code}" -s -o /dev/null) + +if [ "$STATUS" != "200" ] +then + exit_fail "Expected '200 OK' response. Got instead $STATUS" +fi + + +echo -n "Creating order with non-existing POT..." +STATUS=$(curl 'http://localhost:9966/private/orders' \ + -d '{"create_token":false,"refund_delay":{"d_us":0},"order":{"amount":"TESTKUDOS:7","summary":"3","order_default_money_pot":1}}' \ + -w "%{http_code}" -s -o "$LAST_RESPONSE") + +if [ "$STATUS" != "404" ] +then + cat "$LAST_RESPONSE" >&2 + exit_fail "Expected 404, money pot unknown. got: $STATUS" +fi +echo "OK" + + +echo -n "Creating money pot..." +STATUS=$(curl 'http://localhost:9966/private/pots' \ + -d '{"description":"First pot","pot_name":"#1"}' \ + -w "%{http_code}" -s -o "$LAST_RESPONSE") + +if [ "$STATUS" != "200" ] +then + cat "$LAST_RESPONSE" >&2 + exit_fail "Expected 200, pot created. got: $STATUS" +fi +MONEY_POT=$(jq -r .pot_serial_id < "$LAST_RESPONSE") +echo "OK" + + +echo -n "Creating order with money pot..." +STATUS=$(curl 'http://localhost:9966/private/orders' \ + -d '{"create_token":false,"refund_delay":{"d_us":0},"order":{"amount":"TESTKUDOS:7","summary":"3","order_default_money_pot":'"$MONEY_POT"'}}' \ + -w "%{http_code}" -s -o "$LAST_RESPONSE") + +if [ "$STATUS" != "200" ] +then + cat "$LAST_RESPONSE" >&2 + exit_fail "Expected 200, order created. got: $STATUS" +fi + +ORDER_ID=$(jq -r .order_id < "$LAST_RESPONSE") + +echo "OK" + +echo -n "Checking created order without TOKEN..." +STATUS=$(curl http://localhost:9966/orders/"$ORDER_ID" \ + -w "%{http_code}" -s -o "$LAST_RESPONSE") +PAY_URI=$(jq -r .taler_pay_uri < "$LAST_RESPONSE") +if [ "$PAY_URI" == "null" ] +then + cat "$LAST_RESPONSE" >&2 + exit_fail "Expected non-NULL payuri. got $PAY_URI" +fi +echo "OK" + +NOW=$(date +%s) + +echo -n "Pay first order ${PAY_URI} ..." +taler-wallet-cli \ + --no-throttle \ + --wallet-db="$WALLET_DB" \ + handle-uri "${PAY_URI}" \ + -y 2> wallet-pay1.err > wallet-pay1.log +taler-wallet-cli \ + --no-throttle \ + --wallet-db="$WALLET_DB" \ + run-until-done 2> wallet-finish-pay1.err > wallet-finish-pay1.log +NOW2=$(date +%s) +echo " OK (took $(( NOW2 - NOW )) secs )" + + +echo -n "Checking money pot..." +STATUS=$(curl 'http://localhost:9966/private/pots' \ + -w "%{http_code}" -s -o "$LAST_RESPONSE") + +if [ "$STATUS" != "200" ] +then + cat "$LAST_RESPONSE" >&2 + exit_fail "Expected 200, pot status. got: $STATUS" +fi +MONEY_POT_BALANCE=$(jq -r .pots[0].pot_totals[0] < "$LAST_RESPONSE") + +if [ "$MONEY_POT_BALANCE" != "TESTKUDOS:7" ] +then + exit_fail "Expected 'TESTKUDOS:7' balance. Got instead $MONEY_POT_BALANCE" +fi + +echo "OK" + + +exit 0 + + +# +# WIRE TRANSFER TO MERCHANT AND NOTIFY BACKEND +# + +WIRE_DEADLINE=$(jq -r .contract_terms.wire_transfer_deadline.t_s < "$LAST_RESPONSE") + +NOW=$(date +%s) + +TO_SLEEP=$((1200 + WIRE_DEADLINE - NOW )) +echo "Waiting $TO_SLEEP secs for wire transfer" + +echo -n "Call taler-exchange-aggregator ..." +taler-exchange-aggregator \ + -y \ + -c "$CONF" \ + -T "${TO_SLEEP}"000000 \ + -t \ + -L INFO &> aggregator.log +echo " DONE" +echo -n "Call taler-exchange-transfer ..." +taler-exchange-transfer \ + -c "$CONF" \ + -t \ + -L INFO &> transfer.log +echo " DONE" +echo -n "Give time to Nexus to route the payment to Sandbox..." +# FIXME: trigger immediate update at nexus +# NOTE: once libeufin can do long-polling, we should +# be able to reduce the delay here and run aggregator/transfer +# always in the background via setup +sleep 3 +echo " DONE" + +echo -n "Obtaining wire transfer details from bank ($USE_FAKEBANK)..." + +BANKDATA="$(curl 'http://localhost:8082/accounts/exchange/taler-wire-gateway/history/outgoing?delta=1' -s)" +WTID=$(echo "$BANKDATA" | jq -r .outgoing_transactions[0].wtid) +WURL=$(echo "$BANKDATA" | jq -r .outgoing_transactions[0].exchange_base_url) +CREDIT_AMOUNT=$(echo "$BANKDATA" | jq -r .outgoing_transactions[0].amount) +TARGET_PAYTO=$(echo "$BANKDATA" | jq -r .outgoing_transactions[0].credit_account) + +if [ "$EXCHANGE_URL" != "$WURL" ] +then + exit_fail "Wrong exchange URL in '$BANKDATA' response, expected '$EXCHANGE_URL'" +fi + +echo " OK" + +echo -n "Notifying merchant of correct wire transfer..." + +STATUS=$(curl 'http://localhost:9966/private/transfers' \ + -d '{"credit_amount":"'"$CREDIT_AMOUNT"'","wtid":"'"$WTID"'","payto_uri":"'"$TARGET_PAYTO"'","exchange_url":"'"$WURL"'"}' \ + -m 3 \ + -w "%{http_code}" -s -o "$LAST_RESPONSE") + +if [ "$STATUS" != "204" ] +then + jq . < "$LAST_RESPONSE" + exit_fail "Expected response 204 No content, after providing transfer data. got: $STATUS" +fi + +echo " OK" + +echo -n "Running taler-merchant-depositcheck ..." +set -e +taler-merchant-depositcheck \ + -L INFO \ + -c "$CONF" \ + -T "${TO_SLEEP}"000000 \ + -t &> taler-merchant-depositcheck.log +echo " OK" + +echo -n "Running taler-merchant-reconciliation ..." +set -e +taler-merchant-reconciliation \ + -L INFO \ + -c "$CONF" \ + -T "${TO_SLEEP}"000000 \ + -t &> taler-merchant-reconciliation.log +echo " OK" + + +echo -n "Fetching wire transfers ..." + +STATUS=$(curl 'http://localhost:9966/private/transfers' \ + -w "%{http_code}" -s -o "$LAST_RESPONSE") + +if [ "$STATUS" != "200" ] +then + jq . < "$LAST_RESPONSE" + exit_fail "Expected response 200 Ok. got: $STATUS" +fi + +TRANSFERS_LIST_SIZE=$(jq -r '.transfers | length' < "$LAST_RESPONSE") + +if [ "$TRANSFERS_LIST_SIZE" != "1" ] +then + jq . < "$LAST_RESPONSE" + exit_fail "Expected 1 entry in transfer list. Got: $TRANSFERS_LIST_SIZE" +fi + +echo "OK" + +echo -n "Checking order status ..." +STATUS=$(curl "http://localhost:9966/private/orders/${ORDER_ID}?transfer=YES" \ + -w "%{http_code}" -s -o "$LAST_RESPONSE") +if [ "$STATUS" != "200" ] +then + jq . < "$LAST_RESPONSE" + exit_fail "Expected 200, after order inquiry. got: $STATUS" +fi +DEPOSIT_TOTAL=$(jq -r .deposit_total < "$LAST_RESPONSE") +if [ "$DEPOSIT_TOTAL" == "TESTKUDOS:0" ] +then + jq . < "$LAST_RESPONSE" + exit_fail "Expected non-zero deposit total. got: $DEPOSIT_TOTAL" +fi +echo " OK" + +echo -n "Checking bank account status ..." +if [ 1 = "$USE_FAKEBANK" ] +then + STATUS=$(curl "http://localhost:8082/accounts/fortythree" \ + -w "%{http_code}" \ + -s \ + -o "$LAST_RESPONSE") + if [ "$STATUS" != "200" ] + then + jq . < "$LAST_RESPONSE" + exit_fail "Expected response 200 Ok, getting account status. Got: $STATUS" + fi + BALANCE=$(jq -r .balance.amount < "$LAST_RESPONSE") + if [ "$BALANCE" == "TESTKUDOS:0" ] + then + jq . < "$LAST_RESPONSE" + exit_fail "Wire transfer did not happen. Got: $BALANCE" + fi +else + ACCOUNT_PASSWORD="fortythree:x" + BANK_HOST="localhost:18082" + STATUS=$(curl "http://$ACCOUNT_PASSWORD@$BANK_HOST/accounts/fortythree" \ + -w "%{http_code}" -s -o "$LAST_RESPONSE") + if [ "$STATUS" != "200" ] + then + jq . < "$LAST_RESPONSE" + exit_fail "Expected response 200 Ok, getting account status. Got: $STATUS" + fi + BALANCE=$(jq -r .balance.amount < "$LAST_RESPONSE") + if [ "$BALANCE" == "TESTKUDOS:0" ] + then + jq . < "$LAST_RESPONSE" + exit_fail "Wire transfer did not happen. Got: $BALANCE" + fi +fi +echo " OK" + + +exit 0