From 29c4da730b7feed3ca12ebf2e972c01f0e1a5c06 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Thu, 28 Nov 2013 00:57:43 +0000 Subject: [PATCH 001/154] Nuke everything :bomb: Also, start over again with Cucumber, and import the fixture schemas from before. --- .travis.yml | 13 +- README.md | 7 +- .../_models/bank_account.json | 0 .../_models/bank_account_verification.json | 0 {responses => fixtures}/_models/credit.json | 0 {responses => fixtures}/_models/customer.json | 0 {responses => fixtures}/_models/debit.json | 0 {responses => fixtures}/_models/event.json | 0 .../_models/marketplace.json | 0 {responses => fixtures}/_models/order.json | 0 {responses => fixtures}/_models/refund.json | 0 {responses => fixtures}/_models/reversal.json | 0 {responses => fixtures}/_models/status.json | 0 {responses => fixtures}/_models/token.json | 0 requests/README.md | 9 - requests/_models/address.json | 48 --- requests/_models/credit.json | 43 --- requests/_models/debit.json | 49 --- requests/_models/month.json | 27 -- requests/_models/year.json | 14 - requests/_patch.json | 39 --- requests/bank_account.json | 37 --- .../bank_account_verification.json | 13 - requests/bank_accounts/credit.json | 5 - requests/bank_accounts/debit.json | 5 - requests/card.json | 33 -- requests/cards/debit.json | 5 - requests/credit.json | 17 - requests/credits/reversal.json | 13 - requests/customer.json | 106 ------ requests/customers/bank_accounts.json | 16 - requests/customers/cards.json | 16 - requests/customers/credit.json | 5 - requests/customers/debit.json | 6 - requests/customers/order.json | 15 - requests/debit.json | 17 - requests/debits/refund.json | 18 -- responses/README.md | 56 ---- responses/_models/address.json | 54 ---- responses/_models/card.json | 210 ------------ responses/bank_account_tokens.json | 26 -- responses/bank_account_verifications.json | 31 -- responses/bank_accounts.json | 55 ---- responses/callback.json | 4 - responses/card_tokens.json | 26 -- responses/cards.json | 43 --- responses/credits.json | 49 --- responses/customers.json | 80 ----- responses/debits.json | 49 --- responses/errors.json | 189 ----------- responses/events.json | 24 -- responses/marketplaces.json | 97 ------ responses/orders.json | 61 ---- responses/refunds.json | 37 --- responses/response.json | 95 ------ responses/reversals.json | 37 --- scenarios/.gitignore | 1 - scenarios/README.md | 55 ---- scenarios/bank_account_fixtures.yml | 192 ----------- scenarios/bank_accounts/add_to_customer.yml | 36 --- .../bank_account_tokenization.yml | 69 ---- scenarios/bank_accounts/bank_name.yml | 54 ---- .../bank_accounts/tokenize_without_secret.yml | 28 -- .../bank_accounts/verification_failed.yml | 88 ----- .../bank_accounts/verification_success.yml | 46 --- scenarios/card_fixtures.yml | 68 ---- scenarios/cards/add_to_customer.yml | 19 -- scenarios/cards/avs_postal_match.yml | 77 ----- scenarios/cards/avs_street_match.yml | 80 ----- scenarios/cards/card_brand.yml | 68 ---- scenarios/cards/card_tokenization.yml | 131 -------- scenarios/cards/cvv_match.yml | 71 ---- scenarios/cards/tokenize_without_secret.yml | 27 -- scenarios/cards/update_meta.yml | 106 ------ scenarios/checkout_flow/cancel_an_order.yml | 57 ---- ...isting_buyer_purchases_with_a_new_card.yml | 171 ---------- ..._buyer_purchases_with_an_existing_card.yml | 99 ------ .../new_buyer_purchases_an_item.yml | 192 ----------- scenarios/credits/create_new_bank_account.yml | 22 -- scenarios/credits/credit_bank_account.yml | 89 ----- scenarios/credits/credit_card_fail.yml | 28 -- scenarios/credits/credit_customer.yml | 16 - scenarios/credits/credit_customer_fail.yml | 17 - scenarios/customer_fixtures.yml | 92 ------ scenarios/customers/create.yml | 15 - .../customers/set_default_destination.yml | 17 - scenarios/customers/set_default_source.yml | 54 ---- scenarios/customers/underwrite.yml | 54 ---- .../customers/underwrite_with_destination.yml | 77 ----- scenarios/customers/update.yml | 17 - scenarios/debits/create_new_card.yml | 19 -- scenarios/debits/debit_bank_account.yml | 66 ---- scenarios/debits/debit_card.yml | 28 -- scenarios/debits/debit_customer.yml | 17 - scenarios/debits/debit_customer_fail.yml | 17 - scenarios/orders/basic_usage.yml | 56 ---- scenarios/orders/cannot_over_credit.yml | 56 ---- scenarios/orders/create.yml | 31 -- scenarios/orders/create_refund.yml | 97 ------ scenarios/orders/failed_refund_pending.yml | 108 ------- scenarios/orders/simple_order.yml | 98 ------ .../transaction_inherit_description.yml | 55 ---- scenarios/orders/unverified_merchant.yml | 57 ---- scenarios/refunds/create.yml | 13 - .../refunds/create_after_change_default.yml | 73 ----- scenarios/refunds/fail.yml | 17 - scenarios/resources_redirect.yml | 11 - scenarios/reversals/create.yml | 13 - scenarios/reversals/fail_amount.yml | 15 - scenarios/reversals/failed.yml | 31 -- scenarios/reversals/succeeded.yml | 31 -- scenarios/root_resource.yml | 7 - tester/.gitignore | 2 - tester/Makefile | 22 -- tester/README.md | 65 ---- tester/check_json.sh | 26 -- tester/combine.py | 18 -- tester/fix_json.js | 12 - tester/fix_json.py | 17 - tester/fixture_data.py | 46 --- tester/requirements.txt | 3 - tester/run_scenarios.sh | 29 -- tester/runner.py | 306 ------------------ tester/travis.sh | 30 -- 124 files changed, 6 insertions(+), 5518 deletions(-) rename {responses => fixtures}/_models/bank_account.json (100%) rename {responses => fixtures}/_models/bank_account_verification.json (100%) rename {responses => fixtures}/_models/credit.json (100%) rename {responses => fixtures}/_models/customer.json (100%) rename {responses => fixtures}/_models/debit.json (100%) rename {responses => fixtures}/_models/event.json (100%) rename {responses => fixtures}/_models/marketplace.json (100%) rename {responses => fixtures}/_models/order.json (100%) rename {responses => fixtures}/_models/refund.json (100%) rename {responses => fixtures}/_models/reversal.json (100%) rename {responses => fixtures}/_models/status.json (100%) rename {responses => fixtures}/_models/token.json (100%) delete mode 100644 requests/README.md delete mode 100644 requests/_models/address.json delete mode 100644 requests/_models/credit.json delete mode 100644 requests/_models/debit.json delete mode 100644 requests/_models/month.json delete mode 100644 requests/_models/year.json delete mode 100644 requests/_patch.json delete mode 100644 requests/bank_account.json delete mode 100644 requests/bank_accounts/bank_account_verification.json delete mode 100644 requests/bank_accounts/credit.json delete mode 100644 requests/bank_accounts/debit.json delete mode 100644 requests/card.json delete mode 100644 requests/cards/debit.json delete mode 100644 requests/credit.json delete mode 100644 requests/credits/reversal.json delete mode 100644 requests/customer.json delete mode 100644 requests/customers/bank_accounts.json delete mode 100644 requests/customers/cards.json delete mode 100644 requests/customers/credit.json delete mode 100644 requests/customers/debit.json delete mode 100644 requests/customers/order.json delete mode 100644 requests/debit.json delete mode 100644 requests/debits/refund.json delete mode 100644 responses/README.md delete mode 100644 responses/_models/address.json delete mode 100644 responses/_models/card.json delete mode 100644 responses/bank_account_tokens.json delete mode 100644 responses/bank_account_verifications.json delete mode 100644 responses/bank_accounts.json delete mode 100644 responses/callback.json delete mode 100644 responses/card_tokens.json delete mode 100644 responses/cards.json delete mode 100644 responses/credits.json delete mode 100644 responses/customers.json delete mode 100644 responses/debits.json delete mode 100644 responses/errors.json delete mode 100644 responses/events.json delete mode 100644 responses/marketplaces.json delete mode 100644 responses/orders.json delete mode 100644 responses/refunds.json delete mode 100644 responses/response.json delete mode 100644 responses/reversals.json delete mode 100644 scenarios/.gitignore delete mode 100644 scenarios/README.md delete mode 100644 scenarios/bank_account_fixtures.yml delete mode 100644 scenarios/bank_accounts/add_to_customer.yml delete mode 100644 scenarios/bank_accounts/bank_account_tokenization.yml delete mode 100644 scenarios/bank_accounts/bank_name.yml delete mode 100644 scenarios/bank_accounts/tokenize_without_secret.yml delete mode 100644 scenarios/bank_accounts/verification_failed.yml delete mode 100644 scenarios/bank_accounts/verification_success.yml delete mode 100644 scenarios/card_fixtures.yml delete mode 100644 scenarios/cards/add_to_customer.yml delete mode 100644 scenarios/cards/avs_postal_match.yml delete mode 100644 scenarios/cards/avs_street_match.yml delete mode 100644 scenarios/cards/card_brand.yml delete mode 100644 scenarios/cards/card_tokenization.yml delete mode 100644 scenarios/cards/cvv_match.yml delete mode 100644 scenarios/cards/tokenize_without_secret.yml delete mode 100644 scenarios/cards/update_meta.yml delete mode 100644 scenarios/checkout_flow/cancel_an_order.yml delete mode 100644 scenarios/checkout_flow/existing_buyer_purchases_with_a_new_card.yml delete mode 100644 scenarios/checkout_flow/existing_buyer_purchases_with_an_existing_card.yml delete mode 100644 scenarios/checkout_flow/new_buyer_purchases_an_item.yml delete mode 100644 scenarios/credits/create_new_bank_account.yml delete mode 100644 scenarios/credits/credit_bank_account.yml delete mode 100644 scenarios/credits/credit_card_fail.yml delete mode 100644 scenarios/credits/credit_customer.yml delete mode 100644 scenarios/credits/credit_customer_fail.yml delete mode 100644 scenarios/customer_fixtures.yml delete mode 100644 scenarios/customers/create.yml delete mode 100644 scenarios/customers/set_default_destination.yml delete mode 100644 scenarios/customers/set_default_source.yml delete mode 100644 scenarios/customers/underwrite.yml delete mode 100644 scenarios/customers/underwrite_with_destination.yml delete mode 100644 scenarios/customers/update.yml delete mode 100644 scenarios/debits/create_new_card.yml delete mode 100644 scenarios/debits/debit_bank_account.yml delete mode 100644 scenarios/debits/debit_card.yml delete mode 100644 scenarios/debits/debit_customer.yml delete mode 100644 scenarios/debits/debit_customer_fail.yml delete mode 100644 scenarios/orders/basic_usage.yml delete mode 100644 scenarios/orders/cannot_over_credit.yml delete mode 100644 scenarios/orders/create.yml delete mode 100644 scenarios/orders/create_refund.yml delete mode 100644 scenarios/orders/failed_refund_pending.yml delete mode 100644 scenarios/orders/simple_order.yml delete mode 100644 scenarios/orders/transaction_inherit_description.yml delete mode 100644 scenarios/orders/unverified_merchant.yml delete mode 100644 scenarios/refunds/create.yml delete mode 100644 scenarios/refunds/create_after_change_default.yml delete mode 100644 scenarios/refunds/fail.yml delete mode 100644 scenarios/resources_redirect.yml delete mode 100644 scenarios/reversals/create.yml delete mode 100644 scenarios/reversals/fail_amount.yml delete mode 100644 scenarios/reversals/failed.yml delete mode 100644 scenarios/reversals/succeeded.yml delete mode 100644 scenarios/root_resource.yml delete mode 100644 tester/.gitignore delete mode 100644 tester/Makefile delete mode 100644 tester/README.md delete mode 100755 tester/check_json.sh delete mode 100755 tester/combine.py delete mode 100644 tester/fix_json.js delete mode 100644 tester/fix_json.py delete mode 100755 tester/fixture_data.py delete mode 100644 tester/requirements.txt delete mode 100755 tester/run_scenarios.sh delete mode 100755 tester/runner.py delete mode 100755 tester/travis.sh diff --git a/.travis.yml b/.travis.yml index 7a6f6cf..0927861 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,4 @@ -before_install: - - git submodule update --init --recursive -language: python -python: - - 2.7 -install: - - pip install -r tester/requirements.txt -script: - - cd tester && ./travis.sh +language: ruby +rvm: + - 2.0.0 +script: ./bin/cucumber --format=progress diff --git a/README.md b/README.md index 112155b..e44df6f 100644 --- a/README.md +++ b/README.md @@ -40,10 +40,7 @@ the changes. Running Tests ============= -Create a new python2 virtual environment. ``` - $ cd tester - $ pip install -r requirements.txt - $ python fixture_data.py > fixtures.json - $ python runner.py ../scenarios/[scenario name] +$ bundle +$ bin/cucumber ``` diff --git a/responses/_models/bank_account.json b/fixtures/_models/bank_account.json similarity index 100% rename from responses/_models/bank_account.json rename to fixtures/_models/bank_account.json diff --git a/responses/_models/bank_account_verification.json b/fixtures/_models/bank_account_verification.json similarity index 100% rename from responses/_models/bank_account_verification.json rename to fixtures/_models/bank_account_verification.json diff --git a/responses/_models/credit.json b/fixtures/_models/credit.json similarity index 100% rename from responses/_models/credit.json rename to fixtures/_models/credit.json diff --git a/responses/_models/customer.json b/fixtures/_models/customer.json similarity index 100% rename from responses/_models/customer.json rename to fixtures/_models/customer.json diff --git a/responses/_models/debit.json b/fixtures/_models/debit.json similarity index 100% rename from responses/_models/debit.json rename to fixtures/_models/debit.json diff --git a/responses/_models/event.json b/fixtures/_models/event.json similarity index 100% rename from responses/_models/event.json rename to fixtures/_models/event.json diff --git a/responses/_models/marketplace.json b/fixtures/_models/marketplace.json similarity index 100% rename from responses/_models/marketplace.json rename to fixtures/_models/marketplace.json diff --git a/responses/_models/order.json b/fixtures/_models/order.json similarity index 100% rename from responses/_models/order.json rename to fixtures/_models/order.json diff --git a/responses/_models/refund.json b/fixtures/_models/refund.json similarity index 100% rename from responses/_models/refund.json rename to fixtures/_models/refund.json diff --git a/responses/_models/reversal.json b/fixtures/_models/reversal.json similarity index 100% rename from responses/_models/reversal.json rename to fixtures/_models/reversal.json diff --git a/responses/_models/status.json b/fixtures/_models/status.json similarity index 100% rename from responses/_models/status.json rename to fixtures/_models/status.json diff --git a/responses/_models/token.json b/fixtures/_models/token.json similarity index 100% rename from responses/_models/token.json rename to fixtures/_models/token.json diff --git a/requests/README.md b/requests/README.md deleted file mode 100644 index 325a5a6..0000000 --- a/requests/README.md +++ /dev/null @@ -1,9 +0,0 @@ -## Balanced-api requests directory - -The directory defines all the possible requests to the balanced api. - -### layout - * files in this directory are requests that can be done at the top level. Eg: the file `card.json` represents request to `/cards` - * files contained in directories represent nested requests. Eg: `bank_accounts/credit.json` represents requests to `/bank_accounts/:bank_account_id/credits` - * the folder _models contains schemas that are commonly used through requests such as address field - * the _patch.json file represents the schema for a PATCH request to updated an object diff --git a/requests/_models/address.json b/requests/_models/address.json deleted file mode 100644 index ace45b8..0000000 --- a/requests/_models/address.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "line1": { - "type": [ - "string", - "null" - ] - }, - "line2": { - "type": [ - "string", - "null" - ] - }, - "city": { - "type": [ - "string", - "null" - ] - }, - "state": { - "type": [ - "string", - "null" - ] - }, - "postal_code": { - "type": [ - "string", - "null" - ] - }, - "country_code": { - "anyOf": [ - { - "type": "null" - }, - { - "type": "string", - "pattern": "[A-Z]{2}" - } - ] - } - }, - "additionalProperties": false -} \ No newline at end of file diff --git a/requests/_models/credit.json b/requests/_models/credit.json deleted file mode 100644 index 66e7580..0000000 --- a/requests/_models/credit.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "amount": { - "type": "integer", - "minimum": 1, - "required": true - }, - "currency": { - "type": "string", - "enum": [ - "USD" - ] - }, - "order": { - "anyOf": [ - { - "type": "string", - "format": "uri" - }, - { - "type": "null" - } - ] - }, - "appears_on_statement_as": { - "type": [ - "string", - "null" - ] - }, - "description": { - "type": [ - "string", - "null" - ] - }, - "meta": { - "type": "object" - } - } -} \ No newline at end of file diff --git a/requests/_models/debit.json b/requests/_models/debit.json deleted file mode 100644 index f08c3d7..0000000 --- a/requests/_models/debit.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "amount": { - "type": "integer", - "minimum": 1, - "required": true - }, - "currency": { - "type": "string", - "enum": [ - "USD" - ], - "default": "USD" - }, - "order": { - "anyOf": [ - { - "type": "string", - "format": "uri" - }, - { - "type": "null" - } - ] - }, - "appears_on_statement_as": { - "anyOf": [ - { - "type": "string", - "pattern": "[a-zA-Z0-9 \\.\\-]{1,18}" - }, - { - "type": "null" - } - ] - }, - "description": { - "type": [ - "string", - "null" - ] - }, - "meta": { - "type": "object" - } - } -} diff --git a/requests/_models/month.json b/requests/_models/month.json deleted file mode 100644 index 6217c92..0000000 --- a/requests/_models/month.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "anyOf": [ - { - "type": "integer", - "minimum": 1, - "maximum": 12 - }, - { - "type": "string", - "enum": [ - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", - "10", - "11", - "12" - ] - } - ] -} \ No newline at end of file diff --git a/requests/_models/year.json b/requests/_models/year.json deleted file mode 100644 index 350f214..0000000 --- a/requests/_models/year.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "anyOf": [ - { - "type": "integer", - "minimum": 1900, - "maximum": 2100 - }, - { - "type": "string", - "pattern": "[12][901][0-9]{2}" - } - ] -} \ No newline at end of file diff --git a/requests/_patch.json b/requests/_patch.json deleted file mode 100644 index 1691599..0000000 --- a/requests/_patch.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Patch_requests", - "type": "array", - "items": { - "type": "object", - "properties": { - "op": { - "type": "string", - "enum": [ - "add", - "remove", - "replace", - "move", - "copy", - "test" - ], - "required": true - }, - "path": { - "type": "string", - "pattern": "/\\w+s/0/.+", - "required": true - }, - "from": { - "type": "string", - "pattern": "/\\w+s/0/.+", - "required": false - }, - "value": { - "required": false - } - }, - "additionalProperties": false - }, - "required": true, - "uniqueItems": true, - "minItems": 1 -} \ No newline at end of file diff --git a/requests/bank_account.json b/requests/bank_account.json deleted file mode 100644 index bfe16cf..0000000 --- a/requests/bank_account.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "/bank_accounts", - "type": "object", - "properties": { - "name": { - "type": "string", - "required": true - }, - "account_number": { - "type": "string", - "pattern": "[0-9]+", - "required": true - }, - "routing_number": { - "type": "string", - "pattern": "[0-9]{9}", - "required": true - }, - "account_type": { - "type": "string", - "enum": [ - "checking", - "savings" - ], - "required": true - }, - "country_code": { - "type": "string", - "pattern": "[A-Z]{2}", - "default": "US" - }, - "meta": { - "type": "object" - } - } -} \ No newline at end of file diff --git a/requests/bank_accounts/bank_account_verification.json b/requests/bank_accounts/bank_account_verification.json deleted file mode 100644 index 08ea074..0000000 --- a/requests/bank_accounts/bank_account_verification.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "/bank_accounts/:bank_account_id/bank_account_verifications", - "anyof": [ - { - "type": "object", - "properties": {} - }, - { - "type": "null" - } - ] -} \ No newline at end of file diff --git a/requests/bank_accounts/credit.json b/requests/bank_accounts/credit.json deleted file mode 100644 index e12f336..0000000 --- a/requests/bank_accounts/credit.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "/bank_accounts/:bank_account_id/credits", - "$ref": "../_models/credit.json" -} \ No newline at end of file diff --git a/requests/bank_accounts/debit.json b/requests/bank_accounts/debit.json deleted file mode 100644 index 73ff7da..0000000 --- a/requests/bank_accounts/debit.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "/bank_accounts/:bank_account_id/debits", - "$ref": "../_models/debit.json" -} \ No newline at end of file diff --git a/requests/card.json b/requests/card.json deleted file mode 100644 index 48b3e6e..0000000 --- a/requests/card.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "/cards", - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "number": { - "type": "string", - "pattern": "([0-9][ \\-]*){15,16}", - "required": true - }, - "expiration_month": { - "$ref": "_models/month.json", - "required": true - }, - "expiration_year": { - "$ref": "_models/year.json", - "required": true - }, - "cvv": { - "type": "string", - "pattern": "[0-9]{3,4}" - }, - "address": { - "$ref": "_models/address.json" - }, - "meta": { - "type": "object" - } - } -} \ No newline at end of file diff --git a/requests/cards/debit.json b/requests/cards/debit.json deleted file mode 100644 index 87db869..0000000 --- a/requests/cards/debit.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "/cards/:card_id/debits", - "$ref": "../_models/debit.json" -} \ No newline at end of file diff --git a/requests/credit.json b/requests/credit.json deleted file mode 100644 index bd6ca83..0000000 --- a/requests/credit.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "/credits", - "allOf": [ - { - "$ref": "_models/credit.json" - }, - { - "type": "object", - "properties": { - "destination": { - "$ref": "bank_account.json" - } - } - } - ] -} \ No newline at end of file diff --git a/requests/credits/reversal.json b/requests/credits/reversal.json deleted file mode 100644 index afeeb49..0000000 --- a/requests/credits/reversal.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "/credits/:credit_id/reversals", - "anyOf": [ - { - "type": "object", - "properties": {} - }, - { - "type": "null" - } - ] -} \ No newline at end of file diff --git a/requests/customer.json b/requests/customer.json deleted file mode 100644 index 709a3ce..0000000 --- a/requests/customer.json +++ /dev/null @@ -1,106 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "/customers", - "type": "object", - "properties": { - "name": { - "type": [ - "string", - "null" - ] - }, - "business_name": { - "type": [ - "string", - "null" - ] - }, - "email": { - "type": [ - "string", - "null" - ], - "format": "email" - }, - "phone": { - "type": [ - "string", - "null" - ], - "format": "phone" - }, - "ssn_last4": { - "type": [ - "string", - "null" - ], - "pattern": "[0-9]{4}" - }, - "ein": { - "type": [ - "string", - "null" - ], - "pattern": "[0-9]{9}" - }, - "dob_month": { - "anyOf": [ - { - "$ref": "_models/month.json" - }, - { - "type": "null" - } - ] - }, - "dob_year": { - "anyOf": [ - { - "$ref": "_models/year.json" - }, - { - "type": "null" - } - ] - }, - "address": { - "$ref": "_models/address.json" - }, - "source": { - "description": "The default financial instrument used when debiting a customer", - "anyOf": [ - { - "$ref": "card.json" - }, - { - "$ref": "bank_account.json" - }, - { - "description": "Either the href or id of a card or bank account", - "type": "string" - }, - { - "type": "null" - } - ] - }, - "destination": { - "description": "The default financial instrument used when crediting a customer", - "anyOf": [ - { - "$ref": "bank_account.json" - }, - { - "description": "Either the href or the id of a bank account", - "type": "string" - }, - { - "type": "null" - } - ] - }, - "meta": { - "type": "object" - } - } -} \ No newline at end of file diff --git a/requests/customers/bank_accounts.json b/requests/customers/bank_accounts.json deleted file mode 100644 index 1d79cfe..0000000 --- a/requests/customers/bank_accounts.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "/customers/:customer_id/bank_accounts", - "anyOf": [ - { - "$ref": "../bank_account.json" - }, - { - "properties": { - "href": { - "type": "string" - } - } - } - ] -} \ No newline at end of file diff --git a/requests/customers/cards.json b/requests/customers/cards.json deleted file mode 100644 index ef0586d..0000000 --- a/requests/customers/cards.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "/customers/:customer_id/cards", - "anyOf": [ - { - "$ref": "../card.json" - }, - { - "properties": { - "href": { - "type": "string" - } - } - } - ] -} \ No newline at end of file diff --git a/requests/customers/credit.json b/requests/customers/credit.json deleted file mode 100644 index 8fb0732..0000000 --- a/requests/customers/credit.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "/customers/:customer_id/credits", - "$ref": "../_models/credit.json" -} \ No newline at end of file diff --git a/requests/customers/debit.json b/requests/customers/debit.json deleted file mode 100644 index ce1d016..0000000 --- a/requests/customers/debit.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "/customers/:customer_id/debits", - "type": "object", - "$ref": "../_models/debit.json" -} \ No newline at end of file diff --git a/requests/customers/order.json b/requests/customers/order.json deleted file mode 100644 index 65786db..0000000 --- a/requests/customers/order.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "/customers/:customer_id/orders", - "properties": { - "description": { - "type": "string" - }, - "delivery_address": { - "$ref": "../_models/address.json" - }, - "meta": { - "type": "object" - } - } -} \ No newline at end of file diff --git a/requests/debit.json b/requests/debit.json deleted file mode 100644 index d1d5d40..0000000 --- a/requests/debit.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "/debits", - "allOf": [ - { - "$ref": "_models/debit.json" - }, - { - "type": "object", - "properties": { - "source": { - "$ref": "card.json" - } - } - } - ] -} \ No newline at end of file diff --git a/requests/debits/refund.json b/requests/debits/refund.json deleted file mode 100644 index 0f5200c..0000000 --- a/requests/debits/refund.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "/debits/:debit_id/refunds", - "anyOf": [ - { - "type": "object", - "properties": { - "amount": { - "type": "integer", - "minimum": 1 - } - } - }, - { - "type": "null" - } - ] -} diff --git a/responses/README.md b/responses/README.md deleted file mode 100644 index 4d0496a..0000000 --- a/responses/README.md +++ /dev/null @@ -1,56 +0,0 @@ -## Balanced-api responses directory - -This directory defines the form of all the possible responses from the balanced api. - -### Example response -``` json - { - "customers": [ - { - "name": "Balanced testing", - "links": { - "source": null, - "destination": null - }, - "created_at": "2013-08-16T03:19:00.112349Z", - "dob_month": null, - "merchant_status": "need-more-information", - "updated_at": "2013-08-16T03:19:00.112350Z", - "phone": null, - "href": "/customers/CU4CQ5MkeOIuxwL2IGI4I2RG", - "meta": {}, - "dob_year": null, - "email": null, - "address": { - "city": null, - "line2": null, - "line1": null, - "state": null, - "postal_code": null, - "country_code": null - }, - "business_name": null, - "ssn_last4": null, - "id": "CU4CQ5MkeOIuxwL2IGI4I2RG", - "ein": null - } - ], - "links": { - "customers.source": "/cards/{customers.source}", - "customers.card_holds": "/customers/{customers.id}/card_holds", - "customers.cards": "/customers/{customers.id}/cards", - "customers.debits": "/customers/{customers.id}/debits", - "customers.destination": "/cards/{customers.destination}", - "customers.bank_accounts": "/customers/{customers.id}/bank_accounts", - "customers.transactions": "/customers/{customers.id}/transactions", - "customers.refunds": "/customers/{customers.id}/refunds", - "customers.reversals": "/customers/{customers.id}/reversals", - "customers.orders": "/customers/{customers.id}/orders", - "customers.credits": "/customers/{customers.id}/credits" - } - } -``` - -### Layout of files - * The files in this directory are for validating the full response with the response envelope - * The files in the _models directory are for validating the individual object contained in the `[resource]s` array diff --git a/responses/_models/address.json b/responses/_models/address.json deleted file mode 100644 index 99f020d..0000000 --- a/responses/_models/address.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "line1": { - "type": [ - "string", - "null" - ], - "required": true - }, - "line2": { - "type": [ - "string", - "null" - ], - "required": true - }, - "city": { - "type": [ - "string", - "null" - ], - "required": true - }, - "state": { - "type": [ - "string", - "null" - ], - "required": true - }, - "postal_code": { - "type": [ - "string", - "null" - ], - "required": true - }, - "country_code": { - "anyOf": [ - { - "type": "null" - }, - { - "type": "string", - "pattern": "[A-Z]{2}" - } - ], - "required": true - } - }, - "additionalProperties": false -} \ No newline at end of file diff --git a/responses/_models/card.json b/responses/_models/card.json deleted file mode 100644 index d05a5d5..0000000 --- a/responses/_models/card.json +++ /dev/null @@ -1,210 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "id": { - "type": "string", - "required": true, - "pattern": "CC[a-zA-Z0-9]{16,32}" - }, - "href": { - "type": "string", - "format": "uri", - "required": true - }, - "created_at": { - "type": "string", - "format": "date-time", - "required": true - }, - "updated_at": { - "type": "string", - "format": "date-time", - "required": true - }, - "name": { - "type": [ - "string", - "null" - ], - "required": true - }, - "number": { - "type": "string", - "pattern": "x{11,12}[0-9]{4}", - "required": true - }, - "expiration_month": { - "type": "integer", - "minimum": 1, - "maximum": 12, - "required": true - }, - "expiration_year": { - "type": "integer", - "required": true - }, - "cvv": { - "anyOf": [ - { - "type": "string", - "pattern": "x{3,4}" - }, - { - "type": "null" - } - ], - "required": true - }, - "cvv_match": { - "anyOf": [ - { - "type": "string", - "enum": [ - "yes", - "no", - "unsupported" - ] - }, - { - "type": "null" - } - ], - "required": true - }, - "cvv_result": { - "anyOf": [ - { - "type": "string", - "enum": [ - "Suspicious transaction", - "Failed data validation check", - "Match", - "No Match", - "Not Processed", - "Should have been present", - "Issuer unable to process request", - "Card does not support verification" - ] - }, - { - "type": "null" - } - ], - "required": true - }, - "address": { - "$ref": "address.json", - "required": true - }, - "avs_street_match": { - "anyOf": [ - { - "type": "string", - "enum": [ - "yes", - "no", - "unsupported" - ] - }, - { - "type": "null" - } - ], - "required": true - }, - "avs_postal_match": { - "anyOf": [ - { - "type": "string", - "enum": [ - "yes", - "no", - "unsupported" - ] - }, - { - "type": "null" - } - ], - "required": true - }, - "avs_result": { - "anyOf": [ - { - "type": "string", - "enum": [ - "Street address matches, but 5-digit and 9-digit postal code do not match.", - "Street address matches, but postal code not verified.", - "Street address and postal code do not match.", - "Street address and postal code match.", - "AVS data is invalid or AVS is not allowed for this card type.", - "Card member's name does not match, but billing postal code matches.", - "Non-U.S. issuing bank does not support AVS.", - "Card member's name does not match. Street address and postal code match.", - "Address not verified.", - "Card member's name, billing address, and postal code match. Shipping information verified and chargeback protection guaranteed through the Fraud Protection Program.", - "Card member's name matches but billing address and billing postal code do not match.", - "Card member's name and billing postal code match, but billing address does not match.", - "Street address and postal code match.", - "Street address and postal code do not match.", - "Card member's name and billing address match, but billing postal code does not match.", - "Postal code matches, but street address not verified.", - "Card member's name, billing address, and postal code match. Shipping information verified but chargeback protection not guaranteed.", - "System unavailable.", - "U.S.-issuing bank does not support AVS.", - "Card member's name does not match, but street address matches.", - "Address information unavailable.", - "Card member's name, billing address, and billing postal code match.", - "Street address does not match, but 9-digit postal code matches.", - "Street address and 9-digit postal code match.", - "Street address and 5-digit postal code match.", - "Street address does not match, but 5-digit postal code matches." - ] - }, - { - "type": "null" - } - ], - "required": true - }, - "fingerprint": { - "type": "string", - "required": true - }, - "brand": { - "type": "string", - "enum": [ - "Visa", - "MasterCard", - "Discover", - "American Express" - ], - "required": false - }, - "is_verified": { - "type": "boolean", - "required": true - }, - "meta": { - "type": "object", - "required": true - }, - "links": { - "type": "object", - "properties": { - "customer": { - "type": [ - "null", - "string" - ], - "pattern": "(CU|AC)[a-zA-Z0-9]{16,32}", - "required": true - } - }, - "required": true, - "additionalProperties": false - } - }, - "additionalProperties": false -} \ No newline at end of file diff --git a/responses/bank_account_tokens.json b/responses/bank_account_tokens.json deleted file mode 100644 index 48c2fe2..0000000 --- a/responses/bank_account_tokens.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Bank Account tokenization response", - "description": "When using balanced.js or POSTing without an API key to create a Bank Account, this is the schema that is followed instead of the cards.json or bank_accounts.json schema.", - "type": "object", - "properties": { - "links": { - "type": "object", - "required": false - }, - "meta": { - "type": "object", - "required": false - }, - "bank_accounts": { - "items": { - "$ref": "_models/token.json" - }, - "type": "array", - "minItems": 1, - "maxItems": 1, - "uniqueItems": true, - "required": true - } - } -} \ No newline at end of file diff --git a/responses/bank_account_verifications.json b/responses/bank_account_verifications.json deleted file mode 100644 index 04aebb7..0000000 --- a/responses/bank_account_verifications.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "links": { - "type": "object", - "properties": { - "bank_account_verifications.bank_account": { - "type": "string", - "format": "uri", - "pattern": "/bank_accounts/{bank_account_verifications.bank_account}", - "required": true - } - }, - "required": false - }, - "meta": { - "type": "object", - "required": false - }, - "bank_account_verifications": { - "items": { - "$ref": "_models/bank_account_verification.json" - }, - "type": "array", - "minItems": 1, - "uniqueItems": true, - "required": true - } - } -} \ No newline at end of file diff --git a/responses/bank_accounts.json b/responses/bank_accounts.json deleted file mode 100644 index b6954a2..0000000 --- a/responses/bank_accounts.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "links": { - "type": "object", - "properties": { - "bank_accounts.bank_account_verifications": { - "type": "string", - "format": "uri", - "pattern": "/bank_accounts/{bank_accounts.id}/bank_account_verifications", - "required": true - }, - "bank_accounts.credits": { - "type": "string", - "format": "uri", - "pattern": "/bank_accounts/{bank_accounts.id}/credits", - "required": true - }, - "bank_accounts.debits": { - "type": "string", - "format": "uri", - "pattern": "/bank_accounts/{bank_accounts.id}/debits", - "required": true - }, - "bank_accounts.customer": { - "type": "string", - "format": "uri", - "pattern": "/customers/{bank_accounts.customer}", - "required": true - }, - "bank_accounts.bank_account_verification": { - "type": "string", - "format": "uri", - "pattern": "/bank_account_verifications/{bank_accounts.bank_account_verification}", - "required": true - } - }, - "required": false - }, - "meta": { - "type": "object", - "required": false - }, - "bank_accounts": { - "items": { - "$ref": "_models/bank_account.json" - }, - "type": "array", - "minItems": 1, - "uniqueItems": true, - "required": true - } - } -} \ No newline at end of file diff --git a/responses/callback.json b/responses/callback.json deleted file mode 100644 index 21e0870..0000000 --- a/responses/callback.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "$ref": "_models/event.json" -} \ No newline at end of file diff --git a/responses/card_tokens.json b/responses/card_tokens.json deleted file mode 100644 index 7712d25..0000000 --- a/responses/card_tokens.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Card tokenization response", - "description": "When using balanced.js or POSTing without an API key to create a Card, this is the schema that is followed instead of the cards.json or bank_accounts.json schema.", - "type": "object", - "properties": { - "links": { - "type": "object", - "required": false - }, - "meta": { - "type": "object", - "required": false - }, - "cards": { - "items": { - "$ref": "_models/token.json" - }, - "type": "array", - "minItems": 1, - "maxItems": 1, - "uniqueItems": true, - "required": true - } - } -} \ No newline at end of file diff --git a/responses/cards.json b/responses/cards.json deleted file mode 100644 index 3ac1019..0000000 --- a/responses/cards.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "links": { - "type": "object", - "properties": { - "cards.card_holds": { - "type": "string", - "format": "uri", - "pattern": "/cards/{cards.id}/card_holds", - "required": true - }, - "cards.debits": { - "type": "string", - "format": "uri", - "pattern": "/cards/{cards.id}/debits", - "required": true - }, - "cards.customer": { - "type": "string", - "format": "uri", - "pattern": "/customers/{cards.customer}", - "required": true - } - }, - "required": false - }, - "meta": { - "type": "object", - "required": false - }, - "cards": { - "items": { - "$ref": "_models/card.json" - }, - "type": "array", - "minItems": 1, - "uniqueItems": true, - "required": true - } - } -} \ No newline at end of file diff --git a/responses/credits.json b/responses/credits.json deleted file mode 100644 index bfa3c3e..0000000 --- a/responses/credits.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "links": { - "type": "object", - "properties": { - "credits.reversals": { - "type": "string", - "format": "uri", - "pattern": "/credits/{credits.id}/reversals", - "required": true - }, - "credits.customer": { - "type": "string", - "format": "uri", - "pattern": "/customers/{credits.customer}", - "required": true - }, - "credits.order": { - "type": "string", - "format": "uri", - "pattern": "/orders/{credits.order}", - "required": true - }, - "credits.destination": { - "type": "string", - "format": "uri", - "pattern": "/resources/{credits.destination}", - "required": true - } - }, - "required": false - }, - "meta": { - "type": "object", - "required": false - }, - "credits": { - "items": { - "$ref": "_models/credit.json" - }, - "type": "array", - "minItems": 1, - "uniqueItems": true, - "required": true - } - } -} \ No newline at end of file diff --git a/responses/customers.json b/responses/customers.json deleted file mode 100644 index 7c87f4d..0000000 --- a/responses/customers.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "links": { - "type": "object", - "properties": { - "customers.cards": { - "type": "string", - "format": "uri", - "pattern": "/customers/{customers.id}/cards", - "required": true - }, - "customers.bank_accounts": { - "type": "string", - "format": "uri", - "pattern": "/customers/{customers.id}/bank_accounts", - "required": true - }, - "customers.credits": { - "type": "string", - "format": "uri", - "pattern": "/customers/{customers.id}/credits", - "required": true - }, - "customers.debits": { - "type": "string", - "format": "uri", - "pattern": "/customers/{customers.id}/debits", - "required": true - }, - "customers.refunds": { - "type": "string", - "format": "uri", - "pattern": "/customers/{customers.id}/refunds", - "required": true - }, - "customers.reversals": { - "type": "string", - "format": "uri", - "pattern": "/customers/{customers.id}/reversals", - "required": true - }, - "customers.orders": { - "description": "Orders in which the customer is either a buyer or merchant", - "type": "string", - "format": "uri", - "pattern": "/customers/{customers.id}/orders", - "required": true - }, - "customers.source": { - "type": "string", - "format": "uri", - "pattern": "/resources/{customers.source}", - "required": true - }, - "customers.destination": { - "type": "string", - "format": "uri", - "pattern": "/resources/{customers.destination}", - "required": true - } - }, - "required": false - }, - "meta": { - "type": "object", - "required": false - }, - "customers": { - "items": { - "$ref": "_models/customer.json" - }, - "type": "array", - "minItems": 1, - "uniqueItems": true, - "required": true - } - } -} \ No newline at end of file diff --git a/responses/debits.json b/responses/debits.json deleted file mode 100644 index 239f61a..0000000 --- a/responses/debits.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "links": { - "type": "object", - "properties": { - "debits.refunds": { - "type": "string", - "format": "uri", - "pattern": "/debits/{debits.id}/refunds", - "required": true - }, - "debits.customer": { - "type": "string", - "format": "uri", - "pattern": "/customers/{debits.customer}", - "required": true - }, - "debits.order": { - "type": "string", - "format": "uri", - "pattern": "/orders/{debits.order}", - "required": true - }, - "debits.source": { - "type": "string", - "format": "uri", - "pattern": "/resources/{debits.source}", - "required": true - } - }, - "required": false - }, - "meta": { - "type": "object", - "required": false - }, - "debits": { - "items": { - "$ref": "_models/debit.json" - }, - "type": "array", - "minItems": 1, - "uniqueItems": true, - "required": true - } - } -} \ No newline at end of file diff --git a/responses/errors.json b/responses/errors.json deleted file mode 100644 index 2487a5d..0000000 --- a/responses/errors.json +++ /dev/null @@ -1,189 +0,0 @@ -{ - "type": "object", - "properties": { - "status": { - "type": "string", - "required": true - }, - "category_code": { - "type": "string", - "enum": [ - "account-insufficient-funds", - "authorization-failed", - "card-declined", - "funding-destination-declined", - "account-already-merchant", - "address-verification-failed", - "authorization-expired", - "bank-account-already-associated", - "bank-account-authentication-already-exists", - "bank-account-authentication-failed", - "bank-account-authentication-not-pending", - "bank-account-not-associated", - "bank-account-not-valid", - "business-kyc", - "business-principal-kyc", - "cannot-associate-bank-account", - "cannot-associate-card", - "cannot-associate-merchant-with-account", - "cannot-capture-authorization", - "cannot-void-authorization", - "capture-void-attempt", - "card-already-funding-src", - "card-not-associated", - "card-not-valid", - "card-not-validated", - "credit-already-reversed", - "debit-already-refunded", - "debit-not-found", - "duplicate-email-address", - "funding-destination-already-associated", - "funding-destination-cannot-reverse", - "funding-destination-not-associated", - "funding-destination-not-creditable", - "funding-source-already-associated", - "funding-source-not-associated", - "funding-source-not-authorizable", - "funding-source-not-debitable", - "funding-source-not-hold", - "funding-source-not-refundable", - "hold-not-associated-account", - "hold-not-associated-marketplace", - "identity-verification-error", - "insufficient-funds", - "marketplace-already-created", - "marketplace-cannot-prepay-fees", - "multiple-debits", - "no-funding-destination", - "no-funding-source", - "person-kyc", - "precog-logical-error", - "refund-insufficient-funds", - "reverse-void-attempt", - "unexpected-payload", - "bank-account-authentication-forbidden", - "cannot-reverse-credit", - "incomplete-account-info", - "invalid-amount", - "invalid-bank-account-number", - "invalid-reversal-amount", - "invalid-routing-number", - "precog-bad-request", - "refund-invalid-debit-state", - "not-found", - "patch-test-failed", - "request", - "order-kyc", - "method-not-allowed" - ], - "required": true - }, - "description": { - "__example": "Human readable error message about what went wrong. Your request id is OHMasdfasdfasdfasdf", - "anyOf": [ - { - "type": "string", - "enum": [ - "Account balance is insufficient to issue this debit", - "An account with the given email address already exists.", - "Bank account authentication amounts do not match.", - "Bank account authentication amounts do not match.", - "Bank account authentication is not allowed.", - "Bank account authentication is not in state ``pending``.", - "Credit has already been fully reversed.", - "Credit to reversal must have ``succeeded``.", - "Debit has already been fully refunded.", - "Marketplace balance is insufficient to issue this credit", - "Marketplace escrow balance is insufficient to issue this credit.", - "Marketplace escrow balance is insufficient to issue this refund.", - "Marketplace has already been created.", - "No buyer or merchant info was provided. Either \"merchant/merchant_uri\" or\n\"card/card_uri\" fields must be present.", - "Refund amount cannot be greater than the amount of the original debit.", - "Reverse amount cannot be greater than the amount of the original credit.", - "State of debit to refund must be ``succeeded``.", - "The account has no valid funding destinations.", - "The account has no valid funding sources.", - "The account is already a merchant.", - "The bank account is already associated with an account -- it cannot be\nassociated.", - "The bank account is not associated with the given account.", - "The bank account was tokenized on a different marketplace than the one\nused for this request.", - "The card could not be validated -- either the card number or security code\nmay be wrong.", - "The card is already associated with an account -- it cannot be associated\nagain.", - "The card is not associated with the given account.", - "The card was tokenized on a different marketplace than the one used for\nthis request.", - "The given funding destination cannot have a credit created against it.", - "The given funding destination cannot reverse.", - "The given funding destination is already associated with an account.", - "The given funding destination is not associated with the account.", - "The given funding source cannot be refunded.", - "The given funding source cannot have a debit created against it.", - "The given funding source cannot have a hold created against it.", - "The given funding source cannot have an authorization created against\nit. Authorizations are only valid for card-type funding sources.", - "The given funding source is already associated with an account.", - "The given funding source is not associated with an account.", - "The merchant information was created on a different marketplace than the\none used for this request.", - "The processor did not accept the transaction.", - "The processor did not accept this hold.", - "The routing number provided for the bank account was invalid.", - "This address could not be verified.", - "This bank account has already been marked as invalid/deactivated. It cannot\nbe used again.", - "This bank account number is not alpha numeric (ascii). It can not be used.", - "This card has already been marked as invalid/deactivated. It cannot be used\nagain.", - "This card was declined by the processor.", - "This hold has already been captured or voided, and cannot be captured.", - "This hold has already been captured or voided, and cannot be voided.", - "This hold has already been captured, which cannot be reversed.", - "This hold has already been voided, which cannot be reversed.", - "This hold has already expired.", - "This hold is not associated with this account.", - "This hold is not associated with this marketplace.", - "Order escrow balanced is insufficient to issue this credit.", - "Order escrow balanced is insufficient to issue this refund.", - "Order requires merchant be underwritten." - ] - }, - { - "type": "string", - "pattern": ".* Your request id is OHM[a-zA-Z0-9]*\\." - }, - { - "type": "null" - } - ], - "required": true - }, - "additional": { - "required": false - }, - "extras": { - "required": false - }, - "status_code": { - "type": "integer", - "enum": [ - 400, - 402, - 403, - 405, - 404, - 409 - ], - "required": true - }, - "category_type": { - "type": "string", - "enum": [ - "request", - "banking", - "logical" - ], - "required": true - }, - "request_id": { - "type": "string", - "pattern": "OHM[a-zA-Z0-9]{16,34}", - "required": true - } - }, - "additionalProperties": false -} \ No newline at end of file diff --git a/responses/events.json b/responses/events.json deleted file mode 100644 index 9e884a2..0000000 --- a/responses/events.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "links": { - "type": "object", - "properties": {}, - "required": false - }, - "meta": { - "type": "object", - "required": false - }, - "events": { - "items": { - "$ref": "_models/event.json" - }, - "type": "array", - "minItems": 1, - "uniqueItems": true, - "required": true - } - } -} \ No newline at end of file diff --git a/responses/marketplaces.json b/responses/marketplaces.json deleted file mode 100644 index 011052c..0000000 --- a/responses/marketplaces.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "links": { - "type": "object", - "properties": { - "marketplaces.debits": { - "type": "string", - "format": "uri", - "pattern": "/debits", - "required": true - }, - "marketplaces.reversals": { - "type": "string", - "format": "uri", - "pattern": "/reversals", - "required": true - }, - "marketplaces.customers": { - "type": "string", - "format": "uri", - "pattern": "/customers", - "required": true - }, - "marketplaces.credits": { - "type": "string", - "format": "uri", - "pattern": "/credits", - "required": true - }, - "marketplaces.cards": { - "type": "string", - "format": "uri", - "pattern": "/cards", - "required": true - }, - "marketplaces.card_holds": { - "type": "string", - "format": "uri", - "pattern": "/card_holds", - "required": true - }, - "marketplaces.refunds": { - "type": "string", - "format": "uri", - "pattern": "/refunds", - "required": true - }, - "marketplaces.transactions": { - "type": "string", - "format": "uri", - "pattern": "/transactions", - "required": true - }, - "marketplaces.bank_accounts": { - "type": "string", - "format": "uri", - "pattern": "/bank_accounts", - "required": true - }, - "marketplaces.callbacks": { - "type": "string", - "format": "uri", - "pattern": "/callbacks", - "required": true - }, - "marketplaces.events": { - "type": "string", - "format": "uri", - "pattern": "/events", - "required": true - }, - "marketplaces.owner_customer": { - "type": "string", - "format": "uri", - "pattern": "/customers/{marketplaces.owner_customer}", - "required": true - } - }, - "required": false - }, - "meta": { - "type": "object", - "required": false - }, - "marketplaces": { - "items": { - "$ref": "_models/marketplace.json" - }, - "type": "array", - "minItems": 1, - "uniqueItems": true, - "required": true - } - } -} \ No newline at end of file diff --git a/responses/orders.json b/responses/orders.json deleted file mode 100644 index fa61f33..0000000 --- a/responses/orders.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "links": { - "type": "object", - "properties": { - "orders.debits": { - "type": "string", - "format": "uri", - "pattern": "/orders/{orders.id}/debits", - "required": true - }, - "orders.refunds": { - "type": "string", - "format": "uri", - "pattern": "/orders/{orders.id}/refunds", - "required": true - }, - "orders.credits": { - "type": "string", - "format": "uri", - "pattern": "/orders/{orders.id}/credits", - "required": true - }, - "orders.reversals": { - "type": "string", - "format": "uri", - "pattern": "/orders/{orders.id}/reversals", - "required": true - }, - "orders.buyers": { - "type": "string", - "format": "uri", - "pattern": "/orders/{orders.id}/buyers", - "required": true - }, - "orders.merchant": { - "type": "string", - "format": "uri", - "pattern": "/customers/{orders.merchant}", - "required": true - } - }, - "required": false - }, - "meta": { - "type": "object", - "required": false - }, - "orders": { - "items": { - "$ref": "_models/order.json" - }, - "type": "array", - "minItems": 1, - "uniqueItems": true, - "required": true - } - } -} \ No newline at end of file diff --git a/responses/refunds.json b/responses/refunds.json deleted file mode 100644 index 395a9ae..0000000 --- a/responses/refunds.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "links": { - "type": "object", - "properties": { - "refunds.debit": { - "type": "string", - "format": "uri", - "pattern": "/debits/{refunds.debit}", - "required": true - }, - "refunds.order": { - "type": "string", - "format": "uri", - "pattern": "/orders/{refunds.order}", - "required": true - } - }, - "required": false - }, - "meta": { - "type": "object", - "required": false - }, - "refunds": { - "items": { - "$ref": "_models/refund.json" - }, - "type": "array", - "minItems": 1, - "uniqueItems": true, - "required": true - } - } -} \ No newline at end of file diff --git a/responses/response.json b/responses/response.json deleted file mode 100644 index 0ed9c8c..0000000 --- a/responses/response.json +++ /dev/null @@ -1,95 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "links": { - "type": "object", - "required": false - }, - "meta": { - "type": "object", - "required": false - }, - "bank_accounts": { - "items": { - "$ref": "_models/bank_accounts.json" - }, - "type": "array", - "minItems": 1, - "uniqueItems": true - }, - "bank_account_verifications": { - "items": { - "$ref": "_models/bank_account_verifications.json" - }, - "type": "array", - "minItems": 1, - "uniqueItems": true - }, - "card_holds": { - "items": { - "$ref": "_models/card_holds.json" - }, - "type": "array", - "minItems": 1, - "uniqueItems": true - }, - "cards": { - "items": { - "$ref": "_models/cards.json" - }, - "type": "array", - "minItems": 1, - "uniqueItems": true - }, - "credits": { - "items": { - "$ref": "_models/credits.json" - }, - "type": "array", - "minItems": 1, - "uniqueItems": true - }, - "customers": { - "items": { - "$ref": "_models/customers.json" - }, - "type": "array", - "minItems": 1, - "uniqueItems": true - }, - "debits": { - "items": { - "$ref": "_models/debits.json" - }, - "type": "array", - "minItems": 1, - "uniqueItems": true - }, - "orders": { - "items": { - "$ref": "_models/orders.json" - }, - "type": "array", - "minItems": 1, - "uniqueItems": true - }, - "refunds": { - "items": { - "$ref": "_models/refunds.json" - }, - "type": "array", - "minItems": 1, - "uniqueItems": true - }, - "reversals": { - "items": { - "$ref": "_models/reversals.json" - }, - "type": "array", - "minItems": 1, - "uniqueItems": true - } - }, - "additionalProperties": false -} \ No newline at end of file diff --git a/responses/reversals.json b/responses/reversals.json deleted file mode 100644 index 2a2f9b8..0000000 --- a/responses/reversals.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "links": { - "type": "object", - "properties": { - "reversals.credit": { - "type": "string", - "format": "uri", - "pattern": "/credits/{reversals.credit}", - "required": true - }, - "reversals.order": { - "type": "string", - "format": "uri", - "pattern": "/orders/{reversals.order}", - "required": true - } - }, - "required": false - }, - "meta": { - "type": "object", - "required": false - }, - "reversals": { - "items": { - "$ref": "_models/reversal.json" - }, - "type": "array", - "minItems": 1, - "uniqueItems": true, - "required": true - } - } -} \ No newline at end of file diff --git a/scenarios/.gitignore b/scenarios/.gitignore deleted file mode 100644 index a6c57f5..0000000 --- a/scenarios/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.json diff --git a/scenarios/README.md b/scenarios/README.md deleted file mode 100644 index b98f39d..0000000 --- a/scenarios/README.md +++ /dev/null @@ -1,55 +0,0 @@ -## Balanced-api scenarios directory - -This directory contains all the scenarios that defined how the api is suppose to interact. - -### Simple example scenario -``` yaml -scenarios: - - name: customer # Every scenario has a name that can be used to reference it in other scenarios - request: - method: POST - href: /customers - schema: # The request is validated against the request json schema - "$ref": "../requests/customer.json" - body: { # The contents of the the request - "name": "Balanced testing" - } - response: - status_code: 201 # status_code validated the returned status code - schema: # response schema to validate response against - "$ref": "../responses/customers.json" -``` - - -### More complex scenario -``` yaml -require: # scenarios can require the result from other files - - ../card_fixtures.yml - - ../customer_fixtures.yml -scenarios: - - name: add_card_to_customer - request: - method: PATCH - href: "{card,cards.href}" # the results of the other scenarios can be referenced using {scenario_name,resource_name.field_on_resource} - schema: - "$ref": "requests/_patch.json" - body: [{ - "op": "replace", - "path": "/cards/0/links/customer", - "value": "{customer,customers.id}" # referencing other scenarios can also be used in request bodies - }] - response: - schema: - "$ref": "responses/cards.json" - matches: { "cards": [ { "links": { "customer": "{customer,customers.id}" } } ] } # matches assert that a field in the response is equal -``` - - -## Additional notes about scenarios - * required scenarios only run once at the beginning of the file - * scenarios in a file are run sequentially, the file will stop on the first scenario to fail - * when referencing other scenarios by name, the most recent scenario with that name is references (you can overwrite the result of a scenario) - * all required files are isolated from each other (eg, if both files require a customer scenario, then two different customers will be created. Additionally "{customer,customers.href}" will reference the last customer scenario) - * matches in the response will assert that the field in the response and the value given are equal - * assertRegex in the response will assert that the field in the response matches the regular expression - * all paths are relative or from the root of this repository diff --git a/scenarios/bank_account_fixtures.yml b/scenarios/bank_account_fixtures.yml deleted file mode 100644 index c6446e0..0000000 --- a/scenarios/bank_account_fixtures.yml +++ /dev/null @@ -1,192 +0,0 @@ -require: - - ./customer_fixtures.yml -scenarios: - - name: bank_account - request: - method: POST - href: /bank_accounts - schema: - "$ref": "requests/bank_account.json" - body: { - "name": "Kareem Abdul-Jabbar", - "account_number": "9900000000", - "routing_number": "021000021", - "account_type": "checking" - } - response: - status_code: 201 - schema: - "$ref": "responses/bank_accounts.json" - - - name: verified_bank_account - request: - method: POST - href: /bank_accounts - body: { - "name": "Wit Chamberlain", - "account_number": "9900000001", - "routing_number": "321174851", - "account_type": "checking" - } - response: - status_code: 201 - schema: - "$ref": "responses/bank_accounts.json" - - - name: associate_verified_bank_account_with_customer - request: - method: PUT - href: "{verified_bank_account,bank_accounts.href}" - body: { - "links": { - "customer": "{customer,customers.id}" - } - } - response: - schema: - "$ref": "responses/bank_accounts.json" - - - name: verified_bank_account_create_verification - request: - method: POST - href: "{verified_bank_account,bank_accounts.bank_account_verifications}" - response: - status_code: 201 - - - name: verified_bank_account_verify_verification - request: - method: PUT - href: "{verified_bank_account_create_verification,bank_account_verifications.href}" - body: { - "amount_1": 1, - "amount_2": 1 - } - response: - status_code: 200 - matches: { "bank_account_verifications": [ { "verification_status": "succeeded" } ] } - - - name: bank_account_successful - request: - method: POST - href: /bank_accounts - body: { - "name": "Michael Jordan", - "account_number": "9900000002", - "routing_number": "021000021", - "account_type": "checking" - } - response: - status_code: 201 - - - name: verified_bank_account_successful - request: - method: POST - href: /bank_accounts - body: { - "name": "Larry Bird", - "account_number": "9900000003", - "routing_number": "321174851", - "account_type": "checking" - } - response: - status_code: 201 - - - name: associate_verified_bank_account_successful_with_customer - request: - method: PATCH - href: "{verified_bank_account_successful,bank_accounts.href}" - schema: - "$ref": "requests/_patch.json" - body: [{ - "op": "replace", - "path": "/bank_accounts/0/links/customer", - "value": "{customer,customers.id}" - }] - response: - status_code: 200 - schema: - "$ref": "responses/bank_accounts.json" - matches: {"bank_accounts": [{ - "links": { - "customer": "{customer,customers.id}" - } - }]} - - - name: verified_bank_account_successful_create_verification - request: - method: POST - href: "{verified_bank_account_successful,bank_accounts.bank_account_verifications}" - response: - status_code: 201 - - - name: verified_bank_account_successful_verify_verification - request: - method: PUT - href: "{verified_bank_account_successful_create_verification,bank_account_verifications.href}" - body: { - "amount_1": 1, - "amount_2": 1 - } - response: - status_code: 200 - matches: { "bank_account_verifications": [ { "verification_status": "succeeded" } ] } - - - name: bank_account_failed - request: - method: POST - href: /bank_accounts - body: { - "name": "Karl Malone", - "account_number": "9900000004", - "routing_number": "021000021", - "account_type": "checking" - } - response: - status_code: 201 - - - name: verified_bank_account_failed - request: - method: POST - href: /bank_accounts - body: { - "name": "Karl Malone", - "account_number": "9900000004", - "routing_number": "021000021", - "account_type": "checking" - } - response: - status_code: 201 - - - name: associate_verified_bank_account_failed_with_customer - request: - method: PATCH - href: "{verified_bank_account_failed,bank_accounts.href}" - schema: - "$ref": "requests/_patch.json" - body: [{ - "op": "replace", - "path": "/bank_accounts/0/links/customer", - "value": "{customer,customers.id}" - }] - response: - schema: - "$ref": "responses/bank_accounts.json" - - - name: verified_bank_account_failed_create_verification - request: - method: POST - href: "{verified_bank_account_failed,bank_accounts.bank_account_verifications}" - response: - status_code: 201 - - - name: verified_bank_account_failed_verify_verification - request: - method: PUT - href: "{verified_bank_account_failed_create_verification,bank_account_verifications.href}" - body: { - "amount_1": 1, - "amount_2": 1 - } - response: - status_code: 200 - matches: { "bank_account_verifications": [ { "verification_status": "succeeded" } ] } diff --git a/scenarios/bank_accounts/add_to_customer.yml b/scenarios/bank_accounts/add_to_customer.yml deleted file mode 100644 index 3de3970..0000000 --- a/scenarios/bank_accounts/add_to_customer.yml +++ /dev/null @@ -1,36 +0,0 @@ -require: - - ../customer_fixtures.yml - - ../bank_account_fixtures.yml -scenarios: - - - name: add_bank_account_to_customer_1 - request: - method: PATCH - href: "{bank_account,bank_accounts.href}" - schema: - "$ref": "requests/_patch.json" - body: [{ - "op": "replace", - "path": "/bank_accounts/0/links/customer", - "value": "{customer,customers.id}" - }] - response: - schema: - "$ref": "responses/bank_accounts.json" - matches: { "bank_accounts": [ { "links": { "customer": "{customer,customers.id}" } } ] } - - - name: add_bank_account_to_customer - request: - method: PATCH - href: "{verified_bank_account_successful,bank_accounts.href}" - schema: - "$ref": "requests/_patch.json" - body: [{ - "op": "replace", - "path": "/bank_accounts/0/links/customer", - "value": "{customer,customers.id}" - }] - response: - schema: - "$ref": "responses/bank_accounts.json" - matches: { "bank_accounts": [ { "links": { "customer": "{customer,customers.id}" } } ] } \ No newline at end of file diff --git a/scenarios/bank_accounts/bank_account_tokenization.yml b/scenarios/bank_accounts/bank_account_tokenization.yml deleted file mode 100644 index c1eea5e..0000000 --- a/scenarios/bank_accounts/bank_account_tokenization.yml +++ /dev/null @@ -1,69 +0,0 @@ -require: - - ../bank_account_fixtures.yml -scenarios: - - name: retrieve_bank_account - request: - method: GET - href: "{bank_account,bank_accounts.href}" - response: - schema: - "$ref": "responses/bank_accounts.json" - status_code: 200 - matches: {"bank_accounts": [{ - "name": "Kareem Abdul-Jabbar", - "account_number": "xxxxxx0000", - "routing_number": "021000021", - "account_type": "checking", - "bank_name": "JPMORGAN CHASE BANK", - "meta": {} - }]} - - - name: savings_account - request: - method: POST - href: /bank_accounts - schema: - "$ref": "requests/bank_account.json" - body: { - "name": "Jack Lalanne", - "account_number": "200938202", - "routing_number": "121042882", - "account_type": "savings" - } - response: - schema: - "$ref": "responses/bank_accounts.json" - status_code: 201 - matches: {"bank_accounts": [{"account_type": "savings"}]} - - - name: bank_account_address - request: - method: POST - href: /bank_accounts - schema: - "$ref": "requests/bank_account.json" - body: { - "name": "Mahmoud Abdelkader", - "account_number": "200938202", - "routing_number": "121042882", - "account_type": "checking", - "address": { - "country_code": "US", - } - } - response: - schema: - "$ref": "responses/bank_accounts.json" - status_code: 201 - matches: {"bank_accounts": [{ - "address": { - "country_code": "US" - } - }]} - - - name: unstore_bank_account - request: - method: DELETE - href: "{bank_account,bank_accounts.href}" - response: - status_code: 204 diff --git a/scenarios/bank_accounts/bank_name.yml b/scenarios/bank_accounts/bank_name.yml deleted file mode 100644 index b3cb9ae..0000000 --- a/scenarios/bank_accounts/bank_name.yml +++ /dev/null @@ -1,54 +0,0 @@ -scenarios: - - name: bank_name_wells_fargo - request: - method: POST - href: /bank_accounts - schema: - "$ref": "requests/bank_account.json" - body: { - "name": "Jack Lalanne", - "account_number": "200938202", - "routing_number": "121042882", - "account_type": "checking" - } - response: - schema: - "$ref": "responses/bank_accounts.json" - status_code: 201 - matches: {"bank_accounts": [{"bank_name": "WELLS FARGO BANK NA"}]} - - - name: bank_name_boa - request: - method: POST - href: /bank_accounts - schema: - "$ref": "requests/bank_account.json" - body: { - "name": "Michael Johnson", - "account_number": "982379283", - "routing_number": "121000358", - "account_type": "checking" - } - response: - schema: - "$ref": "responses/bank_accounts.json" - status_code: 201 - matches: {"bank_accounts": [{"bank_name": "BANK OF AMERICA, N.A."}]} - - - name: bank_name_jpmc - request: - method: POST - href: /bank_accounts - schema: - "$ref": "requests/bank_account.json" - body: { - "name": "Maurice Green", - "account_number": "33727930", - "routing_number": "322271627", - "account_type": "checking" - } - response: - schema: - "$ref": "responses/bank_accounts.json" - status_code: 201 - matches: {"bank_accounts": [{"bank_name": "J.P. MORGAN CHASE BANK, N.A."}]} diff --git a/scenarios/bank_accounts/tokenize_without_secret.yml b/scenarios/bank_accounts/tokenize_without_secret.yml deleted file mode 100644 index 986d556..0000000 --- a/scenarios/bank_accounts/tokenize_without_secret.yml +++ /dev/null @@ -1,28 +0,0 @@ -scenarios: - - name: tokenize - options: - - no-secret-key - request: - method: POST - href: /bank_accounts - schema: - "$ref": "requests/bank_account.json" - body: { - "name": "That guy over there", - "routing_number": "321174851", - "account_number": "9900000001", - "account_type": "checking" - } - response: - status_code: 201 - schema: - "$ref": "responses/bank_account_tokens.json" - - - name: associated_bank_account_to_marketplace - request: - method: GET - href: "{tokenize,bank_accounts.href}" - response: - status_code: 200 - schema: - "$ref": "responses/bank_accounts.json" \ No newline at end of file diff --git a/scenarios/bank_accounts/verification_failed.yml b/scenarios/bank_accounts/verification_failed.yml deleted file mode 100644 index 3ba5b16..0000000 --- a/scenarios/bank_accounts/verification_failed.yml +++ /dev/null @@ -1,88 +0,0 @@ -require: - - ../bank_account_fixtures.yml -scenarios: - - name: associate_bank_account_with_customer - request: - method: PATCH - href: "{bank_account,bank_accounts.href}" - schema: - "$ref": "requests/_patch.json" - body: [{ - "op": "replace", - "path": "/bank_accounts/0/links/customer", - "value": "{customer,customers.id}" - }] - response: - schema: - "$ref": "responses/bank_accounts.json" - - - name: create_verification - request: - method: POST - href: "{bank_account,bank_accounts.bank_account_verifications}" - schema: - "$ref": "requests/bank_accounts/bank_account_verification.json" - response: - status_code: 201 - schema: - "$ref": "responses/bank_account_verifications.json" - matches: { "bank_account_verifications": [ { "attempts_remaining": 3, "verification_status": "pending" } ] } - - - name: submit_verification1 - request: - method: PUT - href: "{create_verification,bank_account_verifications.href}" - schema: - "$ref": "requests/bank_accounts/bank_account_verification.json" - body: { - "amount_1": 10, - "amount_2": 15 - } - response: - status_code: 409 - schema: - "$ref": "responses/errors.json" - assertRegex: { "description": "Authentication amounts do not match" } - - - name: submit_verification2 - request: - method: PUT - href: "{create_verification,bank_account_verifications.href}" - schema: - "$ref": "requests/bank_accounts/bank_account_verification.json" - body: { - "amount_1": 33, - "amount_2": 42 - } - response: - status_code: 409 - schema: - "$ref": "responses/errors.json" - assertRegex: { "description": "Authentication amounts do not match" } - - - name: submit_verification3 - request: - method: PUT - href: "{create_verification,bank_account_verifications.href}" - schema: - "$ref": "requests/bank_accounts/bank_account_verification.json" - body: { - "amount_1": 99, - "amount_2": 21 - } - response: - status_code: 409 - schema: - "$ref": "responses/errors.json" - assertRegex: { "description": "Authentication amounts do not match" } - - - - name: check_verification - request: - method: GET - href: "{create_verification,bank_account_verifications.href}" - response: - status_code: 200 - schema: - "$ref": "responses/bank_account_verifications.json" - matches: { "bank_account_verifications": [ { "attempts_remaining": 0, "verification_status": "failed" } ] } diff --git a/scenarios/bank_accounts/verification_success.yml b/scenarios/bank_accounts/verification_success.yml deleted file mode 100644 index f07c639..0000000 --- a/scenarios/bank_accounts/verification_success.yml +++ /dev/null @@ -1,46 +0,0 @@ -require: - - ../bank_account_fixtures.yml -scenarios: - - - name: associate_bank_account_to_verify_with_customer - request: - method: PATCH - href: "{bank_account,bank_accounts.href}" - schema: - "$ref": "requests/_patch.json" - body: [{ - "op": "replace", - "path": "/bank_accounts/0/links/customer", - "value": "{customer,customers.id}" - }] - response: - schema: - "$ref": "responses/bank_accounts.json" - - - name: create_verification - request: - method: POST - href: "{bank_account,bank_accounts.bank_account_verifications}" - schema: - "$ref": "requests/bank_accounts/bank_account_verification.json" - response: - status_code: 201 - schema: - "$ref": "responses/bank_account_verifications.json" - matches: { "bank_account_verifications": [ { "attempts_remaining": 3, "verification_status": "pending" } ] } - - - name: submit_verification - request: - method: PUT - href: "{create_verification,bank_account_verifications.href}" - schema: - "$ref": "requests/bank_accounts/bank_account_verification.json" - body: { - "amount_1": 1, - "amount_2": 1 - } - response: - status_code: 200 - schema: - "$ref": "responses/bank_account_verifications.json" - matches: { "bank_account_verifications": [ { "verification_status": "succeeded" } ] } diff --git a/scenarios/card_fixtures.yml b/scenarios/card_fixtures.yml deleted file mode 100644 index 002b936..0000000 --- a/scenarios/card_fixtures.yml +++ /dev/null @@ -1,68 +0,0 @@ -require: - - ./customer_fixtures.yml -scenarios: - - name: card - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "4111 1111 1111 1111", - "expiration_month": 12, - "expiration_year": 2016, - "cvv": "123", - "address": { - "line1": "965 Mission St", - "postal_code": "94103" - } - } - response: - status_code: 201 - schema: - "$ref": "responses/cards.json" - - - name: customer_card - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "4111 1111 1111 1111", - "expiration_month": 12, - "expiration_year": 2016, - } - response: - status_code: 201 - schema: - "$ref": "responses/cards.json" - - - name: associate_customer_card_with_customer - request: - method: PUT - href: "{customer,customers.href}" - schema: - "$ref": "requests/customer.json" - body: { - "card_uri": "{customer_card,cards.href}" - } - response: - schema: - "$ref": "responses/customers.json" - - - name: card_processor_failure - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "4444444444444448", - "expiration_month": 12, - "expiration_year": 2018 - } - response: - status_code: 201 - schema: - "$ref": "responses/cards.json" diff --git a/scenarios/cards/add_to_customer.yml b/scenarios/cards/add_to_customer.yml deleted file mode 100644 index 5dd3b9a..0000000 --- a/scenarios/cards/add_to_customer.yml +++ /dev/null @@ -1,19 +0,0 @@ -require: - - ../card_fixtures.yml - - ../customer_fixtures.yml -scenarios: - - name: add_card_to_customer - request: - method: PATCH - href: "{card,cards.href}" - schema: - "$ref": "requests/_patch.json" - body: [{ - "op": "replace", - "path": "/cards/0/links/customer", - "value": "{customer,customers.id}" - }] - response: - schema: - "$ref": "responses/cards.json" - matches: { "cards": [ { "links": { "customer": "{customer,customers.id}" } } ] } diff --git a/scenarios/cards/avs_postal_match.yml b/scenarios/cards/avs_postal_match.yml deleted file mode 100644 index 390419f..0000000 --- a/scenarios/cards/avs_postal_match.yml +++ /dev/null @@ -1,77 +0,0 @@ -scenarios: - - name: avs_postal_match_yes - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "4111111111111111", - "expiration_month": 12, - "expiration_year": 2016, - "address": { - "postal_code": "94301" - } - } - response: - schema: - "$ref": "responses/cards.json" - status_code: 201 - matches: {"cards": [{"avs_postal_match": "yes"}]} - - - name: avs_postal_match_no - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "4111111111111111", - "expiration_month": 12, - "expiration_year": 2016, - "address": { - "postal_code": "90210" - } - } - response: - schema: - "$ref": "responses/cards.json" - status_code: 201 - matches: {"cards": [{"avs_postal_match": "no"}]} - - - name: avs_postal_match_unsupported - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "4111111111111111", - "expiration_month": 12, - "expiration_year": 2016, - "address": { - "postal_code": "90211" - } - } - response: - schema: - "$ref": "responses/cards.json" - status_code: 201 - matches: {"cards": [{"avs_postal_match": "unsupported"}]} - - - name: avs_postal_match_unused - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "4111111111111111", - "expiration_month": 12, - "expiration_year": 2016, - } - response: - schema: - "$ref": "responses/cards.json" - status_code: 201 - matches: {"cards": [{"avs_postal_match": null}]} diff --git a/scenarios/cards/avs_street_match.yml b/scenarios/cards/avs_street_match.yml deleted file mode 100644 index 6b81379..0000000 --- a/scenarios/cards/avs_street_match.yml +++ /dev/null @@ -1,80 +0,0 @@ -scenarios: - - name: avs_street_match_yes - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "4111111111111111", - "expiration_month": 12, - "expiration_year": 2016, - "address": { - "line1": "965 Mission St", - "postal_code": "94103" - } - } - response: - schema: - "$ref": "responses/cards.json" - status_code: 201 - matches: {"cards": [{"avs_street_match": "yes"}]} - - - name: avs_street_match_no - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "4111111111111111", - "expiration_month": 12, - "expiration_year": 2016, - "address": { - "line1": "21 Jump St", - "postal_code": "90210" - } - } - response: - schema: - "$ref": "responses/cards.json" - status_code: 201 - matches: {"cards": [{"avs_street_match": "no"}]} - - - name: avs_street_match_unsupported - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "4111111111111111", - "expiration_month": 12, - "expiration_year": 2016, - "address": { - "line1": "1984 Elm St", - "postal_code": "90210" - } - } - response: - schema: - "$ref": "responses/cards.json" - status_code: 201 - matches: {"cards": [{"avs_street_match": "unsupported"}]} - - - name: avs_street_match_null - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "4111111111111111", - "expiration_month": 12, - "expiration_year": 2016, - } - response: - schema: - "$ref": "responses/cards.json" - status_code: 201 - matches: {"cards": [{"avs_street_match": null}]} diff --git a/scenarios/cards/card_brand.yml b/scenarios/cards/card_brand.yml deleted file mode 100644 index e80a36f..0000000 --- a/scenarios/cards/card_brand.yml +++ /dev/null @@ -1,68 +0,0 @@ -scenarios: - - name: card_brand_visa - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "4111 1111 1111 1111", - "expiration_month": 12, - "expiration_year": 2016, - } - response: - schema: - "$ref": "responses/cards.json" - status_code: 201 - matches: {"cards": [{"brand": "Visa"}]} - - - name: card_brand_mc - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "5105 1051 0510 5100", - "expiration_month": 12, - "expiration_year": 2016, - } - response: - schema: - "$ref": "responses/cards.json" - status_code: 201 - matches: {"cards": [{"brand": "MasterCard"}]} - - - name: card_brand_amex - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "3782 822463 10005", - "expiration_month": 12, - "expiration_year": 2016, - } - response: - schema: - "$ref": "responses/cards.json" - status_code: 201 - matches: {"cards": [{"brand": "American Express"}]} - - - name: card_brand_discover - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "6011 1111 1111 1117", - "expiration_month": 12, - "expiration_year": 2016, - } - response: - schema: - "$ref": "responses/cards.json" - status_code: 201 - matches: {"cards": [{"brand": "Discover"}]} \ No newline at end of file diff --git a/scenarios/cards/card_tokenization.yml b/scenarios/cards/card_tokenization.yml deleted file mode 100644 index 609f62e..0000000 --- a/scenarios/cards/card_tokenization.yml +++ /dev/null @@ -1,131 +0,0 @@ -require: - - ../card_fixtures.yml -scenarios: - - name: retrieve_card - request: - method: GET - href: "{card,cards.href}" - response: - schema: - "$ref": "responses/cards.json" - status_code: 200 - matches: {"cards": [{ - "name": null, - "number": "xxxxxxxxxxxx1111", - "expiration_month": 12, - "expiration_year": 2016, - "cvv": xxx, - "cvv_match": "yes", - "cvv_result": "Match", - "address": { - "line1": "965 Mission St", - "line2": null, - "city": null, - "state": null, - "postal_code": "94103", - "country_code": null - }, - "avs_street_match": "yes", - "avs_postal_match": "yes", - "avs_result": "Postal code matches, but street address not verified.", - "brand": "Visa", - "meta": {} - }]} - - - name: expiration_string - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "4111 1111 1111 1111", - "expiration_month": "12", - "expiration_year": "2016" - } - response: - schema: - "$ref": "responses/cards.json" - status_code: 201 - matches: {"cards": [{ - "expiration_month": 12, - "expiration_year": 2016 - }]} - - - name: cardholder_name - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "name": "Frida Kahlo", - "number": "4111 1111 1111 1111", - "expiration_month": 12, - "expiration_year": 2016 - } - response: - schema: - "$ref": "responses/cards.json" - status_code: 201 - matches: {"cards": [{"name": "Frida Kahlo"}]} - - - name: cardholder_address - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "4111 1111 1111 1111", - "expiration_month": 12, - "expiration_year": 2016, - "address": { - "line1": "7 Bis Rue de l'Abbé de l'Épée", - "line2": "Apt 4", - "city": "Versailles", - "postal_code": "78000", - "country_code": "FR" - } - } - response: - schema: - "$ref": "responses/cards.json" - status_code: 201 - matches: { - "cards": [{ - "address": { - "line1": "7 Bis Rue de l'Abbé de l'Épée", - "line2": "Apt 4", - "city": "Versailles", - "state": null, - "postal_code": "78000", - "country_code": "FR" - } - }] - } - - - name: fails_luhn - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "4111 1111 1111 1112", - "expiration_month": 12, - "expiration_year": 2016 - } - response: - schema: - "$ref": "responses/errors.json" - status_code: 409 - matches: { "category_code": "card-not-validated" } - assertRegex: { "description": "^Card cannot be validated" } - - - name: unstore_card - request: - method: DELETE - href: "{card,cards.href}" - response: - status_code: 204 diff --git a/scenarios/cards/cvv_match.yml b/scenarios/cards/cvv_match.yml deleted file mode 100644 index 5e0f751..0000000 --- a/scenarios/cards/cvv_match.yml +++ /dev/null @@ -1,71 +0,0 @@ -scenarios: - - name: cvv_match_yes - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "4111111111111111", - "expiration_month": 12, - "expiration_year": 2016, - "cvv": "123" - } - response: - schema: - "$ref": "responses/cards.json" - status_code: 201 - matches: {"cards": [{"cvv_match": "yes"}]} - - - name: cvv_match_no - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "4111111111111111", - "expiration_month": 12, - "expiration_year": 2016, - "cvv": "902" - } - response: - schema: - "$ref": "responses/cards.json" - status_code: 201 - matches: {"cards": [{"cvv_match": "no"}]} - - - name: cvv_match_unsupported - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "4111111111111111", - "expiration_month": 12, - "expiration_year": 2016, - "cvv": "901" - } - response: - schema: - "$ref": "responses/cards.json" - status_code: 201 - matches: {"cards": [{"cvv_match": "unsupported"}]} - - - name: cvv_match_unused - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "4111111111111111", - "expiration_month": 12, - "expiration_year": 2016 - } - response: - schema: - "$ref": "responses/cards.json" - status_code: 201 - matches: {"cards": [{"cvv_match": null}]} diff --git a/scenarios/cards/tokenize_without_secret.yml b/scenarios/cards/tokenize_without_secret.yml deleted file mode 100644 index 991d025..0000000 --- a/scenarios/cards/tokenize_without_secret.yml +++ /dev/null @@ -1,27 +0,0 @@ -scenarios: - - name: tokenize - options: - - no-secret-key - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "4111111111111111", - "expiration_month": "12", - "expiration_year": 2016 - } - response: - status_code: 201 - schema: - "$ref": "responses/card_tokens.json" - - - name: associated_card_to_marketplace - request: - method: GET - href: "{tokenize,cards.href}" - response: - status_code: 200 - schema: - "$ref": "responses/cards.json" \ No newline at end of file diff --git a/scenarios/cards/update_meta.yml b/scenarios/cards/update_meta.yml deleted file mode 100644 index 0635fd2..0000000 --- a/scenarios/cards/update_meta.yml +++ /dev/null @@ -1,106 +0,0 @@ -require: - - ../card_fixtures.yml -scenarios: - - name: update_meta_add - request: - href: "{card,cards.href}" - method: PATCH - schema: - "$ref": "requests/_patch.json" - body: [{ - "op": "add", - "path": "/cards/0/meta/asdf", - "value": "the value to be added" - }] - response: - status_code: 200 - schema: - "$ref": "responses/cards.json" - matches: { "cards": [ { "meta": { "asdf": "the value to be added" } } ] } - - - name: update_meta_replace - request: - href: "{card,cards.href}" - method: PATCH - schema: - "$ref": "requests/_patch.json" - body: [{ - "op": "replace", - "path": "/cards/0/meta/asdf", - "value": "new value" - }] - response: - status_code: 200 - schema: - "$ref": "responses/cards.json" - matches: { "cards": [ { "meta": { "asdf": "new value" } } ] } - - - name: update_meta_safe - request: - href: "{card,cards.href}" - method: PATCH - schema: - "$ref": "requests/_patch.json" - body: [{ - "op": "test", - "path": "/cards/0/meta/asdf", - "value": "new value" - },{ - "op": "replace", - "path": "/cards/0/meta/asdf", - "value": "after checking the value" - }] - response: - status_code: 200 - schema: - "$ref": "responses/cards.json" - matches: { "cards": [ { "meta": { "asdf": "after checking the value" } } ] } - - - name: update_meta_test_fail - request: - href: "{card,cards.href}" - method: PATCH - schema: - "$ref": "requests/_patch.json" - body: [{ - "op": "test", - "path": "/cards/0/meta/asdf", - "value": "not the right value" - }] - response: - status_code: 409 - schema: - "$ref": "responses/errors.json" - - - name: update_meta_move - request: - href: "{card,cards.href}" - method: PATCH - schema: - "$ref": "requests/_patch.json" - body: [{ - "op": "move", - "from": "/cards/0/meta/asdf", - "path": "/cards/0/meta/zxcv" - }] - response: - status_code: 200 - schema: - "$ref": "responses/cards.json" - matches: { "cards": [ { "meta": { "zxcv": "after checking the value" } } ] } - - - name: update_meta_remove - request: - href: "{card,cards.href}" - method: PATCH - schema: - "$ref": "requests/_patch.json" - body: [{ - "op": "remove", - "path": "/cards/0/meta/zxcv" - }] - response: - status_code: 200 - schema: - "$ref": "responses/cards.json" - matches: { "cards": [ { "meta": {} } ] } \ No newline at end of file diff --git a/scenarios/checkout_flow/cancel_an_order.yml b/scenarios/checkout_flow/cancel_an_order.yml deleted file mode 100644 index d4e6526..0000000 --- a/scenarios/checkout_flow/cancel_an_order.yml +++ /dev/null @@ -1,57 +0,0 @@ -require: - - new_buyer_purchases_an_item.yml -scenarios: - # The buyer or seller cancel the order prior to the seller being paid - - name: fetch_order - request: - method: GET - href: "{new_order,orders.href}" - response: - status_code: 200 - schema: - "$ref": "responses/orders.json" - matches: {"orders": [{ - "description": "Catherine Malandrino Black Top" - }]} - - # Refund the debit, which leaves the order empty - - name: fetch_debits - request: - method: GET - href: "{fetch_order,orders.debits}" - response: - status_code: 200 - schema: - "$ref": "responses/debits.json" - matches: {"debits": [{ - "amount": 10000, - "description": "Catherine Malandrino Black Top" - }]} - - - name: refund_debit - request: - method: POST - href: "{fetch_debits,debits.refunds}" - schema: - "$ref": "requests/debits/refund.json" - response: - status_code: 201 - matches: {"refunds": [{ - "amount": 10000, - "description": "Catherine Malandrino Black Top" - }]} - - - name: confirm_order_is_canceled - request: - method: GET - href: "{new_order,orders.href}" - response: - status_code: 200 - schema: - "$ref": "responses/orders.json" - matches: {"orders": [{ - "amount": 0, - "amount_escrowed": 0, - "currency": "USD", - "description": "Catherine Malandrino Black Top" - }]} diff --git a/scenarios/checkout_flow/existing_buyer_purchases_with_a_new_card.yml b/scenarios/checkout_flow/existing_buyer_purchases_with_a_new_card.yml deleted file mode 100644 index ec936e5..0000000 --- a/scenarios/checkout_flow/existing_buyer_purchases_with_a_new_card.yml +++ /dev/null @@ -1,171 +0,0 @@ -require: - - ../customer_fixtures.yml -scenarios: - - name: fetch_customer - request: - method: GET - href: "{customer_with_card,customers.href}" - response: - status_code: 200 - schema: - "$ref": "responses/customers.json" - - # Fetch default card for a buyer - - name: fetch_source - request: - method: GET - href: "{fetch_customer,customers.source}" - response: - status_code: 200 - schema: - "$ref": "responses/cards.json" - - # Fetch list of all cards if the buyer wants to use another card - - name: fetch_all_cards - request: - method: GET - href: "{fetch_customer,customers.cards}" - response: - status_code: 200 - schema: - "$ref": "responses/cards.json" - - # Tokenize a new card, check AVS, and add to customer as the new default - - name: tokenize_new_card - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "name": "Darius the Great", - "number": "4111111111111111", - "expiration_month": 12, - "expiration_year": 2016, - "cvv": "123", - "address": { - "line1": "965 Mission St", - "line2": "Suite 425", - "city": "San Francisco", - "state": "CA", - "postal_code": "94103" - } - } - response: - schema: - "$ref": "responses/cards.json" - status_code: 201 - - # Verify that address and CVV match - - name: verify_avs_match - request: - method: GET - href: "{tokenize_new_card,cards.href}" - response: - status_code: 200 - schema: - "$ref": "responses/cards.json" - matches: {"cards": [{ - "avs_street_match": "yes", - "avs_postal_match": "yes", - "cvv_match": "yes" - }]} - - - name: update_default_card - request: - method: PATCH - href: "{fetch_customer,customers.href}" - schema: - "$ref": "requests/_patch.json" - body: [{ - "op": "replace", - "path": "/customers/0/links/source", - "value": "{tokenize_new_card,cards.href}" - }] - response: - status_code: 200 - schema: - "$ref": "responses/customers.json" - matches: {"customers": [{ - "links": { - "source": "{tokenize_new_card,cards.id}" - } - }]} - - # Create the new order and debit the buyer - - name: new_order - request: - method: POST - href: "{underwritten_merchant,customers.orders}" - schema: - "$ref": "requests/customers/order.json" - body: { - "description": "Catherine Malandrino Black Top", - "delivery_address": { - "line1": "965 Mission St", - "line2": "Suite 425", - "city": "San Francisco", - "state": "CA", - "postal_code": "94103" - }, - "meta": { - "listing": "https://www.vaunte.com/items/catherine-malandrino-black-top-42" - } - } - response: - status_code: 201 - schema: - "$ref": "responses/orders.json" - matches: {"orders": [{ - "links":{ - "merchant": "{underwritten_merchant,customers.id}" - } - }]} - - - name: debit_buyer - request: - method: POST - href: "{customer_with_card,customers.debits}" - schema: - "$ref": "requests/customers/debit.json" - body: { - "amount": 10000, - "order": "{new_order,orders.href}", - "appears_on_statement_as": "Vaunte-Alice Ryan" - } - response: - status_code: 201 - schema: - "$ref": "responses/debits.json" - matches: {"debits": [{ - "description": "Catherine Malandrino Black Top", - "appears_on_statement_as": "BAL*Vaunte-Alice Ryan", - "status": "succeeded", - "links": { - "order": "{new_order,orders.id}" - } - }]} - - # Add the shipping information once the item has been shipped using PATCH - - name: update_meta_on_order - request: - method: PUT - href: "{new_order,orders.href}" - body: { - "meta": { - "listing": "https://www.vaunte.com/items/catherine-malandrino-black-top-42", - "courier": "usps", - "tracking_number": "9405510899359008595488" - } - } - response: - status_code: 200 - schema: - "$ref": "responses/orders.json" - matches: {"orders": [{ - "meta": { - "listing": "https://www.vaunte.com/items/catherine-malandrino-black-top-42", - "courier": "usps", - "tracking_number": "9405510899359008595488" - } - }]} diff --git a/scenarios/checkout_flow/existing_buyer_purchases_with_an_existing_card.yml b/scenarios/checkout_flow/existing_buyer_purchases_with_an_existing_card.yml deleted file mode 100644 index f8a5476..0000000 --- a/scenarios/checkout_flow/existing_buyer_purchases_with_an_existing_card.yml +++ /dev/null @@ -1,99 +0,0 @@ -require: - - ../customer_fixtures.yml -scenarios: - - name: fetch_customer - request: - method: GET - href: "{customer_with_card,customers.href}" - response: - status_code: 200 - schema: - "$ref": "responses/customers.json" - - # Fetch default card for a buyer - - name: fetch_source - request: - method: GET - href: "{fetch_customer,customers.source}" - response: - status_code: 200 - schema: - "$ref": "responses/cards.json" - - # Create the new order and debit the buyer - - name: new_order - request: - method: POST - href: "{underwritten_merchant,customers.orders}" - schema: - "$ref": "requests/customers/order.json" - body: { - "description": "Catherine Malandrino Black Top", - "delivery_address": { - "line1": "965 Mission St", - "line2": "Suite 425", - "city": "San Francisco", - "state": "CA", - "postal_code": "94103" - }, - "meta": { - "listing": "https://www.vaunte.com/items/catherine-malandrino-black-top-42" - } - } - response: - status_code: 201 - schema: - "$ref": "responses/orders.json" - matches: {"orders": [{ - "links":{ - "merchant": "{underwritten_merchant,customers.id}" - } - }]} - - - name: debit_buyer - request: - method: POST - href: "{customer_with_card,customers.debits}" - schema: - "$ref": "requests/customers/debit.json" - body: { - "amount": 10000, - "order": "{new_order,orders.href}", - "appears_on_statement_as": "Vaunte-Alice Ryan" - } - response: - status_code: 201 - schema: - "$ref": "responses/debits.json" - matches: {"debits": [{ - "description": "Catherine Malandrino Black Top", - "appears_on_statement_as": "BAL*Vaunte-Alice Ryan", - "status": "succeeded", - "links": { - "order": "{new_order,orders.id}" - } - }]} - - # Add the shipping information once the item has been shipped - - name: update_meta_on_order - request: - method: PUT - href: "{new_order,orders.href}" - body: { - "meta": { - "listing": "https://www.vaunte.com/items/catherine-malandrino-black-top-42", - "courier": "usps", - "tracking_number": "9405510899359008595488" - } - } - response: - status_code: 200 - schema: - "$ref": "responses/orders.json" - matches: {"orders": [{ - "meta": { - "listing": "https://www.vaunte.com/items/catherine-malandrino-black-top-42", - "courier": "usps", - "tracking_number": "9405510899359008595488" - } - }]} diff --git a/scenarios/checkout_flow/new_buyer_purchases_an_item.yml b/scenarios/checkout_flow/new_buyer_purchases_an_item.yml deleted file mode 100644 index 4e94bc2..0000000 --- a/scenarios/checkout_flow/new_buyer_purchases_an_item.yml +++ /dev/null @@ -1,192 +0,0 @@ -require: - - ../customer_fixtures.yml -scenarios: - - # Tokenize card using balanced.js or by POSTing directly from mobile app - - name: new_card_failing_avs - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "name": "Darius the Great", - "number": "4111111111111111", - "expiration_month": 12, - "expiration_year": 2016, - "cvv": "123", - "address": { - "line1": "21 Jump St", - "city": "San Francisco", - "state": "CA", - "postal_code": "90210" - } - } - response: - schema: - "$ref": "responses/cards.json" - status_code: 201 - - # Verify that address and CVV match - - name: verify_avs_match_fails - request: - method: GET - href: "{new_card_failing_avs,cards.href}" - response: - status_code: 200 - schema: - "$ref": "responses/cards.json" - matches: {"cards": [{ - "avs_street_match": "no", - "avs_postal_match": "no", - "cvv_match": "yes" - }]} - - # Tokenize another card using balanced.js or by POSTing directly from mobile app - - name: new_card - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "name": "Darius the Great", - "number": "4111111111111111", - "expiration_month": 12, - "expiration_year": 2016, - "cvv": "123", - "address": { - "line1": "965 Mission St", - "line2": "Suite 425", - "city": "San Francisco", - "state": "CA", - "postal_code": "94103" - } - } - response: - schema: - "$ref": "responses/cards.json" - status_code: 201 - - # Verify that address and CVV match - - name: verify_avs_match - request: - method: GET - href: "{new_card,cards.href}" - response: - status_code: 200 - schema: - "$ref": "responses/cards.json" - matches: {"cards": [{ - "avs_street_match": "yes", - "avs_postal_match": "yes", - "cvv_match": "yes" - }]} - - # Create the new customer and add the new card as their default payment method - - name: new_buyer - request: - method: POST - href: /customers - schema: - "$ref": "requests/customer.json" - body: { - "name": "Darius the Great", - "email": "darius.great@gmail.com", - "source": "{new_card,cards.href}", - "meta": { - "ip_address": "174.240.15.249" - } - } - response: - status_code: 201 - schema: - "$ref": "responses/customers.json" - matches: { - "customers": [{ - "links": { - "source": "{new_card,cards.id}", - "destination": null, - } - }] - } - - # Create the new order and debit the buyer - - name: new_order - request: - method: POST - href: "{underwritten_merchant,customers.orders}" - schema: - "$ref": "requests/customers/order.json" - body: { - "description": "Catherine Malandrino Black Top", - "delivery_address": { - "line1": "965 Mission St", - "line2": "Suite 425", - "city": "San Francisco", - "state": "CA", - "postal_code": "94103" - }, - "meta": { - "listing": "https://www.vaunte.com/items/catherine-malandrino-black-top-42" - } - } - response: - status_code: 201 - schema: - "$ref": "responses/orders.json" - matches: {"orders": [{ - "links":{ - "merchant": "{underwritten_merchant,customers.id}" - } - }]} - - - name: debit_buyer - request: - method: POST - href: "{new_buyer,customers.debits}" - schema: - "$ref": "requests/customers/debit.json" - body: { - "amount": 10000, - "order": "{new_order,orders.href}", - "appears_on_statement_as": "Vaunte-Alice Ryan" - } - response: - status_code: 201 - schema: - "$ref": "responses/debits.json" - matches: {"debits": [{ - "appears_on_statement_as": "BAL*Vaunte-Alice Ryan", - "description": "Catherine Malandrino Black Top", - "status": "succeeded", - "links": { - "order": "{new_order,orders.id}" - } - }]} - - # Add the shipping information once the item has been shipped - - name: update_meta_on_order - request: - method: PUT - href: "{new_order,orders.href}" - body: { - "meta": { - "courier": "usps", - "tracking_number": "9405510899359008595488", - "listing": "https://www.vaunte.com/items/catherine-malandrino-black-top-42" - } - } - response: - status_code: 200 - schema: - "$ref": "responses/orders.json" - matches: { - "orders": [{ - "meta": { - "courier": "usps", - "tracking_number": "9405510899359008595488", - "listing": "https://www.vaunte.com/items/catherine-malandrino-black-top-42" - } - }] - } diff --git a/scenarios/credits/create_new_bank_account.yml b/scenarios/credits/create_new_bank_account.yml deleted file mode 100644 index 833a397..0000000 --- a/scenarios/credits/create_new_bank_account.yml +++ /dev/null @@ -1,22 +0,0 @@ -require: - - ../debits/create_new_card.yml -scenarios: - - name: create_root_credit - request: - method: POST - href: /credits - schema: - "$ref": "requests/credit.json" - body: { - "amount": 1234, - "destination": { - "name": "Kareem Abdul-Jabbar", - "account_number": "9900000000", - "routing_number": "021000021", - "account_type": "checking" - } - } - response: - status_code: 201 - schema: - "$ref": "responses/credits.json" diff --git a/scenarios/credits/credit_bank_account.yml b/scenarios/credits/credit_bank_account.yml deleted file mode 100644 index aa6bb8e..0000000 --- a/scenarios/credits/credit_bank_account.yml +++ /dev/null @@ -1,89 +0,0 @@ -require: - - ../bank_account_fixtures.yml -scenarios: - - name: load_balance_80000 - request: - method: POST - href: "{verified_bank_account_successful,bank_accounts.debits}" - body: {"amount": 80000} - response: - status_code: 201 - - - name: credit_new_bank_account - request: - method: POST - href: /credits - schema: - "$ref": "requests/credit.json" - body: { - "amount": 20000, - "appears_on_statement_as": "Rent My Bike", - "destination": { - "name": "Kareem Abdul-Jabbar", - "account_number": "9900000000", - "routing_number": "021000021", - "account_type": "checking" - } - } - response: - status_code: 201 - schema: - "$ref": "responses/credits.json" - matches: {"credits": [{ - "amount": 20000, - "appears_on_statement_as": "Rent My Bike", - "status": "pending" - }]} - - - name: credit_existing_bank_account - request: - method: POST - href: "{bank_account,bank_accounts.credits}" - schema: - "$ref": "requests/bank_accounts/credit.json" - body: {"amount": 20000} - response: - status_code: 201 - schema: - "$ref": "responses/credits.json" - matches: {"credits": [{"status": "pending"}]} - - - name: credit_bank_account_successful - request: - method: POST - href: "{bank_account_successful,bank_accounts.credits}" - schema: - "$ref": "requests/bank_accounts/credit.json" - body: {"amount": 20000} - response: - status_code: 201 - schema: - "$ref": "responses/credits.json" - matches: {"credits": [{"status": "succeeded"}]} - - - name: credit_bank_account_failed - request: - method: POST - href: "{bank_account_failed,bank_accounts.credits}" - schema: - "$ref": "requests/bank_accounts/credit.json" - body: {"amount": 20000} - response: - status_code: 201 - schema: - "$ref": "responses/credits.json" - matches: {"credits": [{"status": "failed"}]} - - - name: credit_bank_account_insufficient_escrow - request: - method: POST - href: "{bank_account,bank_accounts.credits}" - schema: - "$ref": "requests/bank_accounts/credit.json" - body: {"amount": 99999999 } - response: - status_code: 409 - schema: - "$ref": "responses/errors.json" - assertRegex: { "description": ".*s insufficient to cover transfer of 99999999.*" } - matches: { "category_code": "account-insufficient-funds" } diff --git a/scenarios/credits/credit_card_fail.yml b/scenarios/credits/credit_card_fail.yml deleted file mode 100644 index b0b0b12..0000000 --- a/scenarios/credits/credit_card_fail.yml +++ /dev/null @@ -1,28 +0,0 @@ -require: - - ../card_fixtures.yml -scenarios: - - name: delete_card - request: - method: DELETE - href: "{card,cards.href}" - response: - status_code: 204 - - - name: credit_invalid_card - request: - method: POST - # Do not construct uri with id like this - # doing this can lead to unexpected cases - # there is no credits end point on a card - - href: "/cards/{card,cards.id}/credits" - body: { - "amount": 1234 - } - response: - status_code: 404 - schema: - "$ref": "responses/errors.json" - matches: { - "category_code": "not-found" - } diff --git a/scenarios/credits/credit_customer.yml b/scenarios/credits/credit_customer.yml deleted file mode 100644 index 5a71f20..0000000 --- a/scenarios/credits/credit_customer.yml +++ /dev/null @@ -1,16 +0,0 @@ -require: - - ../bank_accounts/add_to_customer.yml -scenarios: - - name: credit_customer - request: - method: POST - href: "{customer,customers.credits}" - schema: - "$ref": "requests/customers/credit.json" - body: { - "amount": 500 - } - response: - status_code: 201 - schema: - "$ref": "responses/credits.json" diff --git a/scenarios/credits/credit_customer_fail.yml b/scenarios/credits/credit_customer_fail.yml deleted file mode 100644 index 7d060e3..0000000 --- a/scenarios/credits/credit_customer_fail.yml +++ /dev/null @@ -1,17 +0,0 @@ -require: - - ../customer_fixtures.yml -scenarios: - - name: fail_credit_customer - request: - method: POST - href: "{customer,customers.credits}" - schema: - "$ref": "requests/customers/credit.json" - body: { - "amount": 1234 - } - response: - status_code: 409 - schema: - "$ref": "responses/errors.json" - matches: { "category_code": "no-funding-destination" } diff --git a/scenarios/customer_fixtures.yml b/scenarios/customer_fixtures.yml deleted file mode 100644 index 7700c4c..0000000 --- a/scenarios/customer_fixtures.yml +++ /dev/null @@ -1,92 +0,0 @@ -scenarios: - - name: customer - request: - method: POST - href: /customers - schema: - "$ref": "../requests/customer.json" - body: { - "name": "Balanced testing" - } - response: - status_code: 201 - schema: - "$ref": "../responses/customers.json" - - - name: underwritten_merchant - request: - method: POST - href: /customers - schema: - "$ref": "requests/customer.json" - body: { - "name": "Henry Ford", - "dob_month": 7, - "dob_year": 1963, - "address": { - "postal_code": "48120" - } - } - response: - status_code: 201 - schema: - "$ref": "responses/customers.json" - matches: { "customers": [ { "merchant_status": "underwritten" } ] } - - - name: customer_with_card - request: - method: POST - href: /customers - schema: - "$ref": "requests/customer.json" - body: { - "name": "Darius the Great", - "email": "darius.great@gmail.com", - "source": { - "name": "Darius the Great", - "number": "4111111111111111", - "expiration_month": 12, - "expiration_year": 2016, - "cvv": "123", - "address": { - "line1": "965 Mission St", - "line2": "Suite 425", - "city": "San Francisco", - "state": "CA", - "postal_code": "94103" - } - }, - "meta": { - "ip_address": "174.240.15.249" - } - } - response: - status_code: 201 - schema: - "$ref": "responses/customers.json" - - - name: merchant_with_bank_account - request: - method: POST - href: /customers - schema: - "$ref": "requests/customer.json" - body: { - "name": "Henry Ford", - "dob_month": 7, - "dob_year": 1963, - "address": { - "postal_code": "48120" - }, - "destination": { - "name": "Kareem Abdul-Jabbar", - "account_number": "9900000000", - "routing_number": "021000021", - "account_type": "checking" - } - } - response: - status_code: 201 - schema: - "$ref": "responses/customers.json" - matches: { "customers": [ { "merchant_status": "underwritten" } ] } \ No newline at end of file diff --git a/scenarios/customers/create.yml b/scenarios/customers/create.yml deleted file mode 100644 index 3dc748e..0000000 --- a/scenarios/customers/create.yml +++ /dev/null @@ -1,15 +0,0 @@ -scenarios: - - name: customer_create - request: - method: POST - href: /customers - schema: - "$ref": "requests/customer.json" - body: { - "name": "Balanced Testing", - } - response: - status_code: 201 - schema: - "$ref": "responses/customers.json" - matches: { "customers": [ { "name": "Balanced Testing" } ] } diff --git a/scenarios/customers/set_default_destination.yml b/scenarios/customers/set_default_destination.yml deleted file mode 100644 index aa05c0d..0000000 --- a/scenarios/customers/set_default_destination.yml +++ /dev/null @@ -1,17 +0,0 @@ -require: - - ../bank_accounts/add_to_customer.yml -scenarios: - - name: set_default_source - request: - method: PATCH - href: "{customer,customers.href}" - body: [{ - "op": "replace", - "path": "/customers/0/links/destination", - "value": "{bank_account,bank_accounts.id}" - }] - response: - status_code: 200 - schema: - "$ref": "responses/customers.json" - matches: { "customers": [ { "links": { "destination": "{bank_account,bank_accounts.id}" } } ] } \ No newline at end of file diff --git a/scenarios/customers/set_default_source.yml b/scenarios/customers/set_default_source.yml deleted file mode 100644 index 057591d..0000000 --- a/scenarios/customers/set_default_source.yml +++ /dev/null @@ -1,54 +0,0 @@ -require: - - ../cards/add_to_customer.yml -scenarios: - - - name: set_default_source - request: - method: PATCH - href: "{customer,customers.href}" - body: [{ - "op": "replace", - "path": "/customers/0/links/source", - "value": "{card,cards.id}" - }] - response: - status_code: 200 - schema: - "$ref": "responses/customers.json" - matches: { "customers": [ { "links": { "source": "{card,cards.id}" } } ] } - - - name: other_card - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "4111 1111 1111 1111", - "expiration_month": 12, - "expiration_year": 2016, - "cvv": "123", - "address": { - "line1": "965 Mission St", - "postal_code": "94103" - } - } - response: - status_code: 201 - schema: - "$ref": "responses/cards.json" - - - name: set_default_source_using_PUT - request: - method: PUT - href: "{customer,customers.href}" - body: { - "links": { - "source": "{other_card,cards.id}", - } - } - response: - status_code: 200 - schema: - "$ref": "responses/customers.json" - matches: { "customers": [ { "links": { "source": "{other_card,cards.id}" } } ] } diff --git a/scenarios/customers/underwrite.yml b/scenarios/customers/underwrite.yml deleted file mode 100644 index 2dde15f..0000000 --- a/scenarios/customers/underwrite.yml +++ /dev/null @@ -1,54 +0,0 @@ -scenarios: - - name: customer_create - request: - method: POST - href: /customers - schema: - "$ref": "requests/customer.json" - body: { - "name": "Henry Ford" - } - response: - status_code: 201 - schema: - "$ref": "responses/customers.json" - matches: { "customers": [ { "merchant_status": "need-more-information" } ] } - - - name: add_information - request: - method: PUT - href: "{customer_create,customers.href}" - schema: - "$ref": "requests/customer.json" - body: { - "dob_month": 7, - "dob_year": 1963, - "address": { - "postal_code": "48120" - } - } - response: - status_code: 200 - schema: - "$ref": "responses/customers.json" - matches: { "customers": [ { "merchant_status": "underwritten" } ] } - - - name: create_underwritten - request: - method: POST - href: /customers - schema: - "$ref": "requests/customer.json" - body: { - "name": "Henry Ford", - "dob_month": 7, - "dob_year": 1963, - "address": { - "postal_code": "48120" - } - } - response: - status_code: 201 - schema: - "$ref": "responses/customers.json" - matches: { "customers": [ { "merchant_status": "underwritten" } ] } diff --git a/scenarios/customers/underwrite_with_destination.yml b/scenarios/customers/underwrite_with_destination.yml deleted file mode 100644 index 1556595..0000000 --- a/scenarios/customers/underwrite_with_destination.yml +++ /dev/null @@ -1,77 +0,0 @@ -require: - - ../bank_account_fixtures.yml -scenarios: - - name: create_underwritten - request: - method: POST - href: /customers - schema: - "$ref": "requests/customer.json" - body: { - "name": "Henry Ford", - "dob_month": 7, - "dob_year": 1963, - "address": { - "postal_code": "48120" - } - } - response: - status_code: 201 - schema: - "$ref": "responses/customers.json" - matches: { "customers": [ { "merchant_status": "underwritten" } ] } - - - name: add_bank_account - request: - method: PATCH - href: "{bank_account,bank_accounts.href}" - schema: - "$ref": "requests/_patch.json" - body: [{ - "op": "replace", - "path": "/bank_accounts/0/links/customer", - "value": "{create_underwritten,customers.id}" - }] - response: - status_code: 200 - schema: - "$ref": "responses/bank_accounts.json" - matches: { "bank_accounts": [ { "links": { "customer": "{create_underwritten,customers.id}" } } ] } - - - - name: create_underwritten_successful - request: - method: POST - href: /customers - schema: - "$ref": "requests/customer.json" - body: { - "name": "Henry Ford-successful", - "dob_month": 7, - "dob_year": 1963, - "address": { - "postal_code": "48120" - } - } - response: - status_code: 201 - schema: - "$ref": "responses/customers.json" - matches: { "customers": [ { "merchant_status": "underwritten" } ] } - - - name: add_bank_account_successful - request: - method: PATCH - href: "{bank_account_successful,bank_accounts.href}" - schema: - "$ref": "requests/_patch.json" - body: [{ - "op": "replace", - "path": "/bank_accounts/0/links/customer", - "value": "{create_underwritten_successful,customers.id}" - }] - response: - status_code: 200 - schema: - "$ref": "responses/bank_accounts.json" - matches: { "bank_accounts": [ { "links": { "customer": "{create_underwritten_successful,customers.id}" } } ] } diff --git a/scenarios/customers/update.yml b/scenarios/customers/update.yml deleted file mode 100644 index 8c45af8..0000000 --- a/scenarios/customers/update.yml +++ /dev/null @@ -1,17 +0,0 @@ -require: - - create.yml -scenarios: - - name: update_customer - request: - method: PUT - href: "{customer_create,customers.href}" - schema: - "$ref": "requests/customer.json" - body: { - "email": "asdf@balancedpayments.com" - } - response: - status_code: 200 - schema: - "$ref": "responses/customers.json" - matches: { "customers": [ { "email": "asdf@balancedpayments.com" } ] } \ No newline at end of file diff --git a/scenarios/debits/create_new_card.yml b/scenarios/debits/create_new_card.yml deleted file mode 100644 index 143f523..0000000 --- a/scenarios/debits/create_new_card.yml +++ /dev/null @@ -1,19 +0,0 @@ -scenarios: - - name: create_root_debit - request: - method: POST - href: /debits - schema: - "$ref": "requests/debit.json" - body: { - "amount": 1234, - "source": { - "number": "4111111111111111", - "expiration_year": "2018", - "expiration_month": 12 - } - } - response: - status_code: 201 - schema: - "$ref": "responses/debits.json" diff --git a/scenarios/debits/debit_bank_account.yml b/scenarios/debits/debit_bank_account.yml deleted file mode 100644 index 24d4283..0000000 --- a/scenarios/debits/debit_bank_account.yml +++ /dev/null @@ -1,66 +0,0 @@ -# TODO: -# - attempt to debit new bank account. will fail -# - attempt to debit unverified bank account. will fail - -require: - - ../bank_account_fixtures.yml -scenarios: - - name: debit_verified_bank_account - request: - method: POST - href: "{verified_bank_account,bank_accounts.debits}" - schema: - "$ref": "requests/bank_accounts/debit.json" - body: { - "amount": 20000 - } - response: - status_code: 201 - schema: - "$ref": "responses/debits.json" - matches: {"debits": [{"status": "pending"}]} - - - name: debit_verified_bank_account_successful - request: - method: POST - href: "{verified_bank_account_successful,bank_accounts.debits}" - schema: - "$ref": "requests/bank_accounts/debit.json" - body: { - "amount": 20000 - } - response: - status_code: 201 - schema: - "$ref": "responses/debits.json" - matches: {"debits": [{"status": "succeeded"}]} - - - name: debit_verified_bank_account_failed - request: - method: POST - href: "{verified_bank_account_failed,bank_accounts.debits}" - schema: - "$ref": "requests/bank_accounts/debit.json" - body: { - "amount": 20000 - } - response: - status_code: 201 - schema: - "$ref": "responses/debits.json" - matches: {"debits": [{"status": "failed"}]} - - - name: debit_unverified_bank_account - request: - method: POST - href: "{bank_account,bank_accounts.debits}" - schema: - "$ref": "requests/bank_accounts/debit.json" - body: { - "amount": 123 - } - response: - status_code: 409 - schema: - "$ref": "responses/errors.json" - assertRegex: { "description": "^Funding instrument cannot be debited" } diff --git a/scenarios/debits/debit_card.yml b/scenarios/debits/debit_card.yml deleted file mode 100644 index c9d14c9..0000000 --- a/scenarios/debits/debit_card.yml +++ /dev/null @@ -1,28 +0,0 @@ -require: - - ../card_fixtures.yml -scenarios: - - name: debit_card - request: - method: POST - href: "{card,cards.debits}" - schema: - "$ref": "requests/cards/debit.json" - body: {"amount": 20000} - response: - status_code: 201 - schema: - "$ref": "responses/debits.json" - matches: {"debits": [{"status": "succeeded"}]} - - - name: debit_customer_card - request: - method: POST - href: "{customer_card,cards.debits}" - schema: - "$ref": "requests/cards/debit.json" - body: {"amount": 20000} - response: - status_code: 201 - schema: - "$ref": "responses/debits.json" - matches: {"debits": [{"status": "succeeded"}]} diff --git a/scenarios/debits/debit_customer.yml b/scenarios/debits/debit_customer.yml deleted file mode 100644 index fd58e8b..0000000 --- a/scenarios/debits/debit_customer.yml +++ /dev/null @@ -1,17 +0,0 @@ -require: - - ../cards/add_to_customer.yml -scenarios: - - name: debit_customer - request: - method: POST - href: "{customer,customers.debits}" - schema: - "$ref": "requests/customers/debit.json" - body: { - "amount": 1234 - } - response: - status_code: 201 - schema: - "$ref": "responses/debits.json" - matches: { "debits": [ { "links": { "customer": "{customer,customers.id}", "source": "{add_card_to_customer,cards.id}" } } ] } \ No newline at end of file diff --git a/scenarios/debits/debit_customer_fail.yml b/scenarios/debits/debit_customer_fail.yml deleted file mode 100644 index 847c687..0000000 --- a/scenarios/debits/debit_customer_fail.yml +++ /dev/null @@ -1,17 +0,0 @@ -require: - - ../customer_fixtures.yml -scenarios: - - name: fail_debit_customer - request: - method: POST - href: "{customer,customers.debits}" - schema: - "$ref": "requests/customers/debit.json" - body: { - "amount": 8979 - } - response: - status_code: 409 - schema: - "$ref": "responses/errors.json" - assertRegex: { "description": ".*has no funding source" } diff --git a/scenarios/orders/basic_usage.yml b/scenarios/orders/basic_usage.yml deleted file mode 100644 index a44c065..0000000 --- a/scenarios/orders/basic_usage.yml +++ /dev/null @@ -1,56 +0,0 @@ -require: - - ../cards/add_to_customer.yml - - create.yml -scenarios: - - name: create_debit - request: - method: POST - href: "{add_card_to_customer,cards.debits}" - schema: - "$ref": "requests/cards/debit.json" - body: { - "order": "{order,orders.href}", - "amount": 1234 - } - response: - status_code: 201 - schema: - "$ref": "responses/debits.json" - - - name: check_order_escrow_1 - request: - method: GET - href: "{order,orders.href}" - response: - status_code: 200 - schema: - "$ref": "responses/orders.json" - matches: { "orders": [ { "amount_escrowed": 1234 } ] } - - - name: create_credit - request: - method: POST - href: "{verified_bank_account_successful,bank_accounts.credits}" - schema: - "$ref": "requests/bank_accounts/credit.json" - body: { - "order": "{order,orders.href}", - "amount": 1234 - } - response: - status_code: 201 - shcema: - "$ref": "responses/credits.json" - matches: { - "credits": [{"status": "succeeded", "links": {"destination": "{verified_bank_account_successful,bank_accounts.id}"}}] - } - - - name: check_order_escrow_2 - request: - method: GET - href: "{order,orders.href}" - response: - status_code: 200 - schema: - "$ref": "responses/orders.json" - matches: { "orders": [ { "amount_escrowed": 0 } ] } diff --git a/scenarios/orders/cannot_over_credit.yml b/scenarios/orders/cannot_over_credit.yml deleted file mode 100644 index 0272a23..0000000 --- a/scenarios/orders/cannot_over_credit.yml +++ /dev/null @@ -1,56 +0,0 @@ -require: - - ../cards/add_to_customer.yml - - create.yml -# - ../customers/underwrite_with_destination.yml -scenarios: - - name: create_debit - request: - method: POST - href: "{add_card_to_customer,cards.debits}" - schema: - "$ref": "requests/cards/debit.json" - body: { - "order": "{order,orders.href}", - "amount": 1234 - } - response: - status_code: 201 - schema: - "$ref": "responses/debits.json" - - - name: check_order_escrow_1 - request: - method: GET - href: "{order,orders.href}" - response: - status_code: 200 - schema: - "$ref": "responses/orders.json" - matches: { "orders": [ { "amount_escrowed": 1234 } ] } - - - name: create_credit_fail - request: - method: POST - href: "{customer,customers.credits}" - schema: - "$ref": "requests/customers/credit.json" - body: { - "order": "{order,orders.href}", - "amount": 2000 - } - response: - status_code: 409 - shcema: - "$ref": "responses/errors.json" - assertRegex: { "description": ".*is insufficient to cover transfer of 2000.*" } - matches: { "category_code": "account-insufficient-funds" } - - - name: check_order_escrow_2 - request: - method: GET - href: "{order,orders.href}" - response: - status_code: 200 - schema: - "$ref": "responses/orders.json" - matches: { "orders": [ { "amount_escrowed": 1234 } ] } diff --git a/scenarios/orders/create.yml b/scenarios/orders/create.yml deleted file mode 100644 index 8fcdf69..0000000 --- a/scenarios/orders/create.yml +++ /dev/null @@ -1,31 +0,0 @@ -require: - - ../bank_accounts/add_to_customer.yml -scenarios: - - name: underwrite_customer - request: - method: PUT - href: "{customer,customers.href}" - schema: - "$ref": "requests/customer.json" - body: { - "dob_month": 7, - "dob_year": 1963, - "name": "Henry Ford", - "address": { - "postal_code": "48120" - } - } - response: - schema: - "$ref": "responses/customers.json" - matches: { "customers": [ { "merchant_status": "underwritten" } ] } - - name: order - request: - method: POST - href: "{customer,customers.orders}" - schema: - "$ref": "requests/customers/order.json" - response: - status_code: 201 - schema: - "$ref": "responses/orders.json" diff --git a/scenarios/orders/create_refund.yml b/scenarios/orders/create_refund.yml deleted file mode 100644 index fcf94ef..0000000 --- a/scenarios/orders/create_refund.yml +++ /dev/null @@ -1,97 +0,0 @@ -require: - - ../cards/add_to_customer.yml - - create.yml -# - ../customers/underwrite_with_destination.yml -scenarios: - - name: create_debit - request: - method: POST - href: "{add_card_to_customer,cards.debits}" - schema: - "$ref": "requests/cards/debit.json" - body: { - "order": "{order,orders.href}", - "amount": 1234 - } - response: - status_code: 201 - schema: - "$ref": "responses/debits.json" - - - name: check_order_escrow_1 - request: - method: GET - href: "{order,orders.href}" - response: - status_code: 200 - schema: - "$ref": "responses/orders.json" - matches: { "orders": [ { "amount_escrowed": 1234 } ] } - - - name: create_credit - request: - method: POST - href: "{add_bank_account_to_customer,bank_accounts.credits}" - schema: - "$ref": "requests/bank_accounts/credit.json" - body: { - "order": "{order,orders.href}", - "amount": 1234 - } - response: - status_code: 201 - shcema: - "$ref": "responses/credits.json" - - - name: check_order_escrow_2 - request: - method: GET - href: "{order,orders.href}" - response: - status_code: 200 - schema: - "$ref": "responses/orders.json" - matches: { "orders": [ { "amount_escrowed": 0 } ] } - - - name: create_reversal - request: - method: POST - href: "{create_credit,credits.reversals}" - schema: - "$ref": "requests/credits/reversal.json" - response: - status_code: 201 - schema: - "$ref": "responses/reversals.json" - matches: { "reversals": [ { "status": "succeeded" } ] } - - - name: check_order_escrow_3 - request: - method: GET - href: "{order,orders.href}" - response: - status_code: 200 - schema: - "$ref": "responses/orders.json" - matches: { "orders": [ { "amount_escrowed": 1234 } ] } - - - name: create_refund - request: - method: POST - href: "{create_debit,debits.refunds}" - schema: - "$ref": "requests/debits/refund.json" - response: - status_code: 201 - schema: - "$ref": "responses/refunds.json" - - - name: check_order_escrow_4 - request: - method: GET - href: "{order,orders.href}" - response: - status_code: 200 - schema: - "$ref": "responses/orders.json" - matches: { "orders": [ { "amount_escrowed": 0 } ] } diff --git a/scenarios/orders/failed_refund_pending.yml b/scenarios/orders/failed_refund_pending.yml deleted file mode 100644 index f32134b..0000000 --- a/scenarios/orders/failed_refund_pending.yml +++ /dev/null @@ -1,108 +0,0 @@ -require: - - ../cards/add_to_customer.yml - - create.yml -# - ../customers/underwrite_with_destination.yml -scenarios: - - - name: associate_failed_bank_account_with_customer - request: - method: PUT - href: "{bank_account_failed,bank_accounts.href}" - body: { - "links": { - "customer": "{customer,customers.id}" - } - } - response: - schema: - "$ref": "responses/bank_accounts.json" - - - name: create_debit - request: - method: POST - href: "{add_card_to_customer,cards.debits}" - schema: - "$ref": "requests/cards/debit.json" - body: { - "order": "{order,orders.href}", - "amount": 1234 - } - response: - status_code: 201 - schema: - "$ref": "responses/debits.json" - - - name: check_order_escrow_1 - request: - method: GET - href: "{order,orders.href}" - response: - status_code: 200 - schema: - "$ref": "responses/orders.json" - matches: { "orders": [ { "amount_escrowed": 1234 } ] } - - - name: create_failed_credit - request: - method: POST - href: "{bank_account_failed,bank_accounts.credits}" - schema: - "$ref": "requests/bank_accounts/credit.json" - body: { - "order": "{order,orders.href}", - "amount": 1234 - } - response: - status_code: 201 - schema: - "$ref": "responses/credits.json" - martches: { "credits": [ { "status": "failed" } ] } - - - name: check_order_escrow_2 - request: - method: GET - href: "{order,orders.href}" - response: - status_code: 200 - schema: - "$ref": "responses/orders.json" - matches: { "orders": [ { "amount_escrowed": 1234 } ] } - - - name: create_pending_credit - request: - method: POST - href: "{bank_account,bank_accounts.credits}" - schema: - "$ref": "requests/bank_accounts/credit.json" - body: { - "order": "{order,orders.href}", - "amount": 1234 - } - response: - status_code: 201 - schema: - "$ref": "responses/credits.json" - martches: { "credits": [ { "status": "pending" } ] } - - - name: check_order_escrow_2 - request: - method: GET - href: "{order,orders.href}" - response: - status_code: 200 - schema: - "$ref": "responses/orders.json" - matches: { "orders": [ { "amount_escrowed": 0 } ] } - - - name: create_refund_failure - request: - method: POST - href: "{create_debit,debits.refunds}" - schema: - "$ref": "requests/debits/refund.json" - response: - status_code: 409 - schema: - "$ref": "responses/errors.json" - assertRegex: { "description": ".*is insufficient to cover transfer of 1234.*" } - matches: { "category_code": "account-insufficient-funds" } \ No newline at end of file diff --git a/scenarios/orders/simple_order.yml b/scenarios/orders/simple_order.yml deleted file mode 100644 index f406d01..0000000 --- a/scenarios/orders/simple_order.yml +++ /dev/null @@ -1,98 +0,0 @@ -require: - - ../bank_account_fixtures.yml - - ../customer_fixtures.yml - - ../cards/add_to_customer.yml -scenarios: - - name: add_bank_account - request: - method: PUT - href: "{bank_account,bank_accounts.href}" - body: { - "links": { - "customer": "{underwritten_merchant,customers.id}" - } - } - response: - status_code: 200 - - - name: order - request: - method: POST - href: "{underwritten_merchant,customers.orders}" - schema: - "$ref": "requests/customers/order.json" - response: - status_code: 201 - schema: - "$ref": "responses/orders.json" - matches: {"orders": [{ - "links":{ - "merchant": "{underwritten_merchant,customers.id}" - } - }]} - - - name: debit_buyer - request: - method: POST - href: "{customer,customers.debits}" - schema: - "$ref": "requests/customers/debit.json" - body: { - "amount": 10000, - "order": "{order,orders.href}" - } - response: - status_code: 201 - schema: - "$ref": "responses/debits.json" - matches: {"debits": [{ - "links": { - "order": "{order,orders.id}" - } - }]} - - - name: check_escrow_after_debit - request: - method: GET - href: "{order,orders.href}" - response: - status_code: 200 - schema: - "$ref": "responses/orders.json" - matches: {"orders": [{ - "amount": 10000, - "amount_escrowed": 10000, - }]} - - - name: pay_merchant - request: - method: POST - href: "{underwritten_merchant,customers.credits}" - schema: - "$ref": "requests/customers/credit.json" - body: { - "amount": 10000, - "order": "{order,orders.href}" - } - response: - status_code: 201 - schema: - "$ref": "responses/credits.json" - matches: {"credits": [{ - "links": { - "order": "{order,orders.id}" - } - }]} - - - name: check_escrow_after_credit - request: - method: GET - href: "{order,orders.href}" - response: - status_code: 200 - schema: - "$ref": "responses/orders.json" - matches: {"orders": [{ - "amount": 10000, - "amount_escrowed": 0, - }]} \ No newline at end of file diff --git a/scenarios/orders/transaction_inherit_description.yml b/scenarios/orders/transaction_inherit_description.yml deleted file mode 100644 index b6ecaf8..0000000 --- a/scenarios/orders/transaction_inherit_description.yml +++ /dev/null @@ -1,55 +0,0 @@ -require: - - ../customer_fixtures.yml -scenarios: - - name: order_with_description - request: - method: POST - href: "{merchant_with_bank_account,customers.orders}" - schema: - "$ref": "requests/customers/order.json" - body: { - "description": "Beats by Dr. Dre" - } - response: - status_code: 201 - schema: - "$ref": "responses/orders.json" - matches: {"orders":[{ - "description": "Beats by Dr. Dre" - }]} - - - name: debit_without_description - request: - method: POST - href: "{customer_with_card,customers.debits}" - schema: - "$ref": "requests/customers/debit.json" - body: { - "amount": 10000, - "order": "{order_with_description,orders.href}" - } - response: - status_code: 201 - schema: - "$ref": "responses/debits.json" - matches: {"debits":[{ - "description": "Beats by Dr. Dre" - }]} - - - name: credit_without_description - request: - method: POST - href: "{merchant_with_bank_account,customers.credits}" - schema: - "$ref": "requests/customers/credit.json" - body: { - "amount": 10000, - "order": "{order_with_description,orders.href}" - } - response: - status_code: 201 - schema: - "$ref": "responses/credits.json" - matches: {"credits":[{ - "description": "Beats by Dr. Dre" - }]} \ No newline at end of file diff --git a/scenarios/orders/unverified_merchant.yml b/scenarios/orders/unverified_merchant.yml deleted file mode 100644 index 96229c0..0000000 --- a/scenarios/orders/unverified_merchant.yml +++ /dev/null @@ -1,57 +0,0 @@ -require: - - ../cards/add_to_customer.yml - - ../bank_accounts/add_to_customer.yml -# - ../customer_fixtures.yml -scenarios: - - name: order - request: - method: POST - href: "{customer,customers.orders}" - response: - status_code: 201 - schema: - "$ref": "responses/orders.json" - - - name: create_debit - request: - method: POST - href: "{add_card_to_customer,cards.debits}" - schema: - "$ref": "requests/cards/debit.json" - body: { - "order": "{order,orders.href}", - "amount": 1234 - } - response: - status_code: 201 - schema: - "$ref": "responses/debits.json" - - - name: check_order_escrow_1 - request: - method: GET - href: "{order,orders.href}" - response: - status_code: 200 - schema: - "$ref": "responses/orders.json" - matches: { "orders": [ { "amount_escrowed": 1234 } ] } - - - name: create_credit - request: - method: POST - href: "{verified_bank_account_successful,bank_accounts.credits}" - schema: - "$ref": "requests/bank_accounts/credit.json" - body: { - "amount": 1234, - "order": "{order,orders.href}" - } - response: - status_code: 409 - schema: - "$ref": "responses/errors.json" - assertRegex: { - "description": "Order requires that merchant CU[a-zA-Z0-9]{16,32} be underwritten.", - "category_code": "order-kyc" - } \ No newline at end of file diff --git a/scenarios/refunds/create.yml b/scenarios/refunds/create.yml deleted file mode 100644 index fadb0ee..0000000 --- a/scenarios/refunds/create.yml +++ /dev/null @@ -1,13 +0,0 @@ -require: - - ../debits/debit_card.yml -scenarios: - - name: create_refund - request: - method: POST - href: "{debit_customer_card,debits.refunds}" - schema: - "$ref": "requests/debits/refund.json" - response: - status_code: 201 - schema: - "$ref": "responses/refunds.json" \ No newline at end of file diff --git a/scenarios/refunds/create_after_change_default.yml b/scenarios/refunds/create_after_change_default.yml deleted file mode 100644 index 17696e1..0000000 --- a/scenarios/refunds/create_after_change_default.yml +++ /dev/null @@ -1,73 +0,0 @@ -require: - - ../customers/set_default_source.yml -scenarios: - - name: create_debit - request: - method: POST - href: "{customer,customers.debits}" - schema: - "$ref": "requests/customers/debit.json" - body: { - "amount": 1234 - } - response: - status_code: 201 - schema: - "$ref": "responses/debits.json" - matches: { "debits": [ { "links": { "source": "{other_card,cards.id}" } } ] } - - - name: new_card - request: - method: POST - href: /cards - schema: - "$ref": "requests/card.json" - body: { - "number": "4111 1111 1111 1111", - "expiration_month": 12, - "expiration_year": 2016, - } - response: - status_code: 201 - schema: - "$ref": "responses/cards.json" - - - name: change_default_source - request: - method: PATCH - href: "{customer,customers.href}" - body: [{ - "op": "replace", - "path": "/customers/0/links/source", - "value": "{new_card,cards.id}" - }] - response: - status_code: 200 - schema: - "$ref": "responses/customers.json" - matches: { "customers": [ { "links": { "source": "{new_card,cards.id}" } } ] } - - # this refund should go back onto the card that - # this debit was create with - # not the new default source on the customer - - name: create_refund - request: - method: POST - href: "{create_debit,debits.refunds}" - schema: - "$ref": "requests/debits/refund.json" - response: - status_code: 201 - schema: - "$ref": "responses/refunds.json" - matches: { "refunds": [ { "links": { "debit": "{create_debit,debits.id}" } } ] } - - - name: check_debit - request: - method: GET - href: "{create_debit,debits.href}" - response: - status_code: 200 - schema: - "$ref": "responses/debits.json" - matches: { "debits": [ { "links": { "source": "{other_card,cards.id}" } } ] } diff --git a/scenarios/refunds/fail.yml b/scenarios/refunds/fail.yml deleted file mode 100644 index 8579dc9..0000000 --- a/scenarios/refunds/fail.yml +++ /dev/null @@ -1,17 +0,0 @@ -require: - - ../debits/debit_card.yml -scenarios: - - name: create_failed_refund - request: - method: POST - href: "{debit_customer_card,debits.refunds}" - schema: - "$ref": "requests/debits/refund.json" - body: { - "amount": 200000000 - } - response: - status_code: 400 - schema: - "$ref": "responses/errors.json" - assertRegex: { "description": ".*must be <= 20000" } diff --git a/scenarios/resources_redirect.yml b/scenarios/resources_redirect.yml deleted file mode 100644 index 829ddc1..0000000 --- a/scenarios/resources_redirect.yml +++ /dev/null @@ -1,11 +0,0 @@ -require: - - customer_fixtures.yml -scenarios: - - name: get_resouces - request: - method: GET - href: "/resources/{customer,customers.id}" - response: - status_code: 200 - schema: - "$ref": "responses/customers.json" \ No newline at end of file diff --git a/scenarios/reversals/create.yml b/scenarios/reversals/create.yml deleted file mode 100644 index c394394..0000000 --- a/scenarios/reversals/create.yml +++ /dev/null @@ -1,13 +0,0 @@ -require: - - ../credits/credit_bank_account.yml -scenarios: - - name: reversals_create - request: - method: POST - href: "{credit_existing_bank_account,credits.reversals}" - schema: - "$ref": "requests/credits/reversal.json" - response: - status_code: 201 - schema: - "$ref": "responses/reversals.json" \ No newline at end of file diff --git a/scenarios/reversals/fail_amount.yml b/scenarios/reversals/fail_amount.yml deleted file mode 100644 index bda2388..0000000 --- a/scenarios/reversals/fail_amount.yml +++ /dev/null @@ -1,15 +0,0 @@ -require: - - ../credits/credit_bank_account.yml -scenarios: - - name: failed_reversal - request: - method: POST - href: "{credit_new_bank_account,credits.reversals}" - schema: - "$ref": "requests/credits/reversal.json" - body: { "amount": 10000000 } - response: - status_code: 400 - schema: - "$ref": "responses/errors.json" - assertRegex: { "description": "^Invalid field \\[amount\\]" } diff --git a/scenarios/reversals/failed.yml b/scenarios/reversals/failed.yml deleted file mode 100644 index 3c3e934..0000000 --- a/scenarios/reversals/failed.yml +++ /dev/null @@ -1,31 +0,0 @@ -scenarios: - - name: credit_bank_account - request: - method: POST - href: /credits - schema: - "$ref": "requests/credit.json" - body: { - "amount": 1234, - "destination": { - "name": "Michael Jordan", - "account_number": "9900000004", - "routing_number": "021000021", - "account_type": "checking" - } - } - response: - status_code: 201 - schema: - "$ref": "responses/credits.json" - - name: create_reversal - request: - method: POST - href: "{credit_bank_account,credits.reversals}" - schema: - "$ref": "requests/credits/reversal.json" - response: - status_code: 201 - schema: - "$ref": "responses/reversals.json" - matches: { "reversals": [ { "status": "failed" } ] } diff --git a/scenarios/reversals/succeeded.yml b/scenarios/reversals/succeeded.yml deleted file mode 100644 index 654994d..0000000 --- a/scenarios/reversals/succeeded.yml +++ /dev/null @@ -1,31 +0,0 @@ -scenarios: - - name: credit_bank_account - request: - method: POST - href: /credits - schema: - "$ref": "requests/credit.json" - body: { - "amount": 1234, - "destination": { - "name": "Michael Jordan", - "account_number": "9900000095", - "routing_number": "021000021", - "account_type": "checking" - } - } - response: - status_code: 201 - schema: - "$ref": "responses/credits.json" - - name: create_reversal - request: - method: POST - href: "{credit_bank_account,credits.reversals}" - schema: - "$ref": "requests/credits/reversal.json" - response: - status_code: 201 - schema: - "$ref": "responses/reversals.json" - matches: { "reversals": [ { "status": "succeeded" } ] } diff --git a/scenarios/root_resource.yml b/scenarios/root_resource.yml deleted file mode 100644 index ef395cb..0000000 --- a/scenarios/root_resource.yml +++ /dev/null @@ -1,7 +0,0 @@ -scenarios: - - name: get_root - request: - method: GET - href: / - response: - status_code: 200 diff --git a/tester/.gitignore b/tester/.gitignore deleted file mode 100644 index 734771b..0000000 --- a/tester/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -fixtures.json -*iml diff --git a/tester/Makefile b/tester/Makefile deleted file mode 100644 index 32f9465..0000000 --- a/tester/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -SCENARIOS= $(shell find ../scenarios/ -name "*.yml") -PYTHON= python - -JSON_SCENARIOS= $(SCENARIOS:.yml=.json) - -ROOT_URL=https://api-pmtest.balancedpayments.com - -all: scenarios.json - -clean: - rm -f fixtures.json - rm -rf $(JSON_SCENARIOS) scenarios.json - -fixtures.json: - @ROOT_URL=$(ROOT_URL) $(PYTHON) fixture_data.py > fixtures.json - -scenarios.json: $(JSON_SCENARIOS) - @ROOT_URL=$(ROOT_URL) $(PYTHON) combine.py $(JSON_SCENARIOS) > $@ - - -%.json: %.yml fixtures.json - @ROOT_URL=$(ROOT_URL) DUMP_JSON=1 $(PYTHON) runner.py $< > $@ 2> /dev/null diff --git a/tester/README.md b/tester/README.md deleted file mode 100644 index bda1211..0000000 --- a/tester/README.md +++ /dev/null @@ -1,65 +0,0 @@ -## Balanced-api test runner directory - -This directory contains all the code for running the scenarios against the api and validating the output. - - -## Files -| Name | Used for | -|--------------------|-------------------------------------------------------| -| `check_json.sh` | Automatically checks and styles all json files | -| `run_scenarios.sh` | Sequentially runs all scenarios against the api | -| `travis.sh` | Checks json and scenarios syntax | -| `Makefile` | Builds scenarios.json for building the docs scenarios | -| `requirements.txt` | All the python requirements for running the scenarios | -| `fixture_data.py` | Creates a new api_key for running the scenarios | -| `fix_json.js` | Checks the json syntax of a single file | -| `runner.py` | Sequentially runs the scenarios in a single file | -| `combine.py` | Combines the json output for building the docs | - - -### Running a single scenario -``` - $ cd tester - $ pip install -r requirements.txt - $ python fixture_data.py > fixtures.json - $ python runner.py ../scenarios/the_name_of_the_file.yml -``` - -### Running all scenarios -``` - $ cd tester - $ pip install -r requirements.txt - $ python fixture_data.py > fixtures.json - $ ./run_scenarios.sh -``` - -### Running scenarios against an alternate host -``` - $ cd tester - $ pip install -r requirements.txt - $ ROOT_URL="https://api.balancedpayments.com" python fixture_data.py > fixtures.json - $ ROOT_URL="https://api.balancedpayments.com" ./run_scenarios.sh -``` - -### Running scenarios against a different api version -``` - $ cd tester - $ pip install -r requirements.txt - $ API_VERSION=1.1 python fixture_data.py > fixtures.json - $ API_VERSION=1.1 ./run_scenarios.sh -``` - -### Checking the syntax of files -``` - $ cd tester - $ pip install -r requirements.txt - $ ./travis.sh -``` - -### Creating scenarios.json for the docs -``` - $ cd tester - $ pip install -r requirements.txt - $ python fixture_data.py > fixtures.json - $ make -``` diff --git a/tester/check_json.sh b/tester/check_json.sh deleted file mode 100755 index b4c4487..0000000 --- a/tester/check_json.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -FAILED="" -code=0 - -FILES=`find .. -name "*.json"` - -for i in $FILES -do - if node fix_json.js $i > /dev/null ; then - echo -e "\e[00;32mOK $i\e[00m" - else - echo -e "\e[00;31mFAILED $i\e[00m" - FAILED="$FAILED\t$i\n" - code=$(( $code + 1 )) - fi -done - -if [ $code -ne 0 ]; then - echo -e "\e[00;31mChecking of JSON has failed\e[00m" - echo -e "$FAILED" -else - echo "ALL passed" -fi - -exit $code diff --git a/tester/combine.py b/tester/combine.py deleted file mode 100755 index 0e47dd5..0000000 --- a/tester/combine.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python - -import json -import sys -import os - -def main(): - data = {} - for name in sys.argv: - if 'combine' in name: - continue - vers, _ = os.path.splitext(os.path.basename(name)) - data[vers] = json.loads(open(name).read()) - print(json.dumps(data, indent=1)) - - -if __name__ == "__main__": - main() diff --git a/tester/fix_json.js b/tester/fix_json.js deleted file mode 100644 index 9e6ac07..0000000 --- a/tester/fix_json.js +++ /dev/null @@ -1,12 +0,0 @@ -var fs = require('fs'); - -var inStr = fs.readFileSync(process.argv[2]).toString(); - -var json = JSON.parse(inStr); - -var str = JSON.stringify(json, null, 4); - -console.log(str); - -if(inStr != str) - fs.writeFileSync(process.argv[2], str); diff --git a/tester/fix_json.py b/tester/fix_json.py deleted file mode 100644 index 12f9a32..0000000 --- a/tester/fix_json.py +++ /dev/null @@ -1,17 +0,0 @@ -import sys -import json - -def main(): - try: - contents = json.load(open(sys.argv[1])) - except ValueError, e: - sys.stderr.write(e.message) - sys.exit(1) - result = json.dumps(contents, indent=4) - print(result) - if contents != result: - with open(sys.argv[1], 'w') as w: - w.write(result) - -if __name__ == '__main__': - main() diff --git a/tester/fixture_data.py b/tester/fixture_data.py deleted file mode 100755 index 9fa545f..0000000 --- a/tester/fixture_data.py +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env python -import os -import json -import requests - -import yaml - - -config = {} -if os.path.isfile('../config.yml'): - config = yaml.load(open('../config.yml').read()) - -ROOT_URL = os.environ.get('ROOT_URL', config.get('root_url', 'http://localhost:5000')) -API_VERSION = os.environ.get('API_VERSION', config.get('api_version', '1.1')) -ACCEPT_HEADERS = os.environ.get( - 'ACCEPT_HEADERS', - config.get('accept_headers', ('application/vnd.balancedpayments+json; version={version}, ' - 'application/vnd.api+json')) -) -ACCEPT_HEADERS = ACCEPT_HEADERS.replace('{version}', API_VERSION) - -cache = {} - -def make_api_key(): - - result = requests.post(ROOT_URL + '/api_keys', headers={'Accept': ACCEPT_HEADERS}, data={}) - - cache['secret'] = result.json()['api_keys'][0]['secret'] - -def make_marketplace(): - - result = requests.post(ROOT_URL + '/marketplaces', auth=(cache['secret'], ''), - headers={'Accept': ACCEPT_HEADERS}, data={}) - - cache['marketplace'] = result.json()['marketplaces'][0]['href'] - -def main(): - - make_api_key() - make_marketplace() - - print(json.dumps(cache, indent=2)) - - -if __name__ == '__main__': - main() diff --git a/tester/requirements.txt b/tester/requirements.txt deleted file mode 100644 index 7d3e349..0000000 --- a/tester/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -pyyaml -requests -jsonschema diff --git a/tester/run_scenarios.sh b/tester/run_scenarios.sh deleted file mode 100755 index ade7ec0..0000000 --- a/tester/run_scenarios.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash - -RED=\033[0;31m -ERED=\033[0m - -FAILED="" -code=0 - -FILES=`find ../scenarios -name "*.yml" | sort` - -for i in $FILES -do - if python runner.py $i; then - echo -e "\033[00;32mOK $i\033[00m" - else - echo -e "\033[00;31mFAILED $i\033[00m" - FAILED="$FAILED\t$i\n" - code=$(( $code + 1 )) - fi -done - -if [ $code -ne 0 ]; then - echo -e "\033[00;31mRunning of scenarios has failed\033[00m" - echo -e "$FAILED" -else - echo "ALL passed" -fi - -exit $code diff --git a/tester/runner.py b/tester/runner.py deleted file mode 100755 index 83a91cc..0000000 --- a/tester/runner.py +++ /dev/null @@ -1,306 +0,0 @@ -#!/usr/bin/env python - -# This file is designed to run one scenario at a time -# scenarios can include in other scenario using a require block at the top -# the scenarios are run in the order that they are specified - -import os -import sys -import re -import json - -import yaml -import requests -import jsonschema - -config = {} -if os.path.isfile('../config.yml'): - config = yaml.load(open('../config.yml').read()) - -ROOT_URL = os.environ.get('ROOT_URL', config.get('root_url', 'http://localhost:5000')) -API_VERSION = os.environ.get('API_VERSION', config.get('api_version', '1.1')) -ACCEPT_HEADERS = os.environ.get( - 'ACCEPT_HEADERS', - config.get('accept_headers', ('application/vnd.balancedpayments+json; version={version}, ' - 'application/vnd.api+json')) -) -ACCEPT_HEADERS = ACCEPT_HEADERS.replace('{version}', API_VERSION) - -DRY_RUN = os.environ.get('DRY_RUN', '0') != '0' - -DUMP_JSON = os.environ.get('DUMP_JSON', '0') != '0' - - -def find_file(fromFile, fileName): - path = os.path.join(os.path.dirname(fromFile), fileName) - if os.path.isfile(path): - return path - path = os.path.join('..', fileName) - if os.path.isfile(path): - return path - sys.stderr.write('Could not find file {0}\n'.format(fileName)) - sys.exit(1) - - -# the $ref do not work with relative paths as specified in the jsonschema spec -def validator_fix_ref(contents, fileName): - if isinstance(contents, dict): - if '$ref' in contents: - path = find_file(fileName, contents['$ref']) - cont = open(path).read() - return validator_fix_ref(json.loads(cont), path) - else: - return dict( - (k, validator_fix_ref(v, fileName)) - for k, v in contents.iteritems() - ) - elif isinstance(contents, list): - return [validator_fix_ref(v, fileName) for v in contents] - else: - return contents - - -def validator_required(validator, required, instance, schema): - # the properties function in draft3 takes care of required - # this validator has an issue with the form of required that we are using - return - -validator = jsonschema.validators.extend(jsonschema.Draft4Validator, - { - "properties": jsonschema._validators.properties_draft3, - "required": validator_required, - }) - - -def validate(resp_json): - class Validator(object): - - def against(self, schema): - validator(schema).validate(resp_json) - return Validator() - - -class Runner(object): - - cache = {} - - session = requests.Session() - - def get_field(self, name, data): - - def resolve_link(match): - if match.group(2) in data[match.group(1)][0]: - return data[match.group(1)][0][match.group(2)] - else: - return data[match.group(1)][0]['links'][match.group(2)] - - controller, action = name.split('.') - try: - if action in data[controller][0]: - return data[controller][0][action] - except Exception as ex: - #sys.stderr.write(ex) - import ipdb; ipdb.set_trace() - if name in data['links']: - return re.sub( - r'\{(\w+)\.(\w+)\}', - resolve_link, - data['links'][name] - ) - return None - - def resolve_deps(self, scenario, data): - def fix_match(matchgroup): - if DRY_RUN: - gg = data[matchgroup.group(1)] - return 'asdf' - return self.get_field( - matchgroup.group(2), data[matchgroup.group(1)]['response'] - ) - if isinstance(scenario, str): - return re.sub(r'\{(\w+),(\w+\.\w+)\}', fix_match, scenario) - elif isinstance(scenario, list): - return [self.resolve_deps(v, data) for v in scenario] - elif isinstance(scenario, dict): - return dict( - (k, self.resolve_deps(v, data)) - for k, v in scenario.iteritems() - ) - else: - return scenario - - def equals(self, data, instance, assertRegex): - ret = 0 - if isinstance(data, dict): - if not isinstance(instance, dict): - return 1 - for k, v in data.iteritems(): - eq = self.equals(v, instance.get(k, None), assertRegex) - ret += eq - if eq: - sys.stderr.write('err: {}[{}] != {}\n'.format( - k, v, instance.get(k) - )) - return ret - elif isinstance(data, list): - if not isinstance(instance, list): - return 1 - if len(instance) < len(data): - return 1 - for i in xrange(len(data)): - ret += self.equals(data[i], instance[i], assertRegex) - return ret - else: - if assertRegex: - try: - if re.match(data, instance): - return 0 - except TypeError: - pass - return 1 - else: - return 0 if data == instance else 1 - - def run_scenario(self, scenario, data, path): - sys.stderr.write('Running scenario {0}\n'.format(scenario['name'])) - - scenario = self.resolve_deps(scenario, data) - - request_scenario = scenario['request'] - response = scenario['response'] - name = scenario['name'] - body = request_scenario.get('body', {}) - options = scenario.get('options', []) - - if 'schema' in request_scenario: - try: - schema = validator_fix_ref(request_scenario['schema'], path) - except Exception, e: - sys.stderr.write( - 'Error loading request schema for {0}'.format(name) - ) - sys.stderr.write(str(e)) - sys.exit(1) - validate(body).against(schema) - else: - if body: - sys.stderr.write( - 'Warning: {0} missing schema section for request\n'.format( - name - ) - ) - if not (isinstance(request_scenario['href'], str) - or isinstance(request_scenario['href'], unicode)): - sys.stderr.write( - 'href for scenario must be string' - ) - sys.exit(1) - - if not DRY_RUN: - sys.stderr.write('{0}: {2} {1}\n'.format( - name, - ROOT_URL + request_scenario['href'], - request_scenario['method'] - )) - kwargs = {} - if 'no-secret-key' not in options: - kwargs['auth'] = (self.cache['secret'], '') - if 'no-follow-redirects' in options: - kwargs['allow_redirects'] = False - resp = requests.request( - method=request_scenario['method'], - url=ROOT_URL + request_scenario['href'], - data=json.dumps(body), - headers={ - 'Accept': ACCEPT_HEADERS, - 'Content-type': 'application/json' - }, - **kwargs - ) - - if 'status_code' in response and not DRY_RUN: - if response['status_code'] != resp.status_code: - sys.stderr.write( - 'Scenario {0} failed with wrong status code {1} != {2}' - .format(name, response['status_code'], resp.status_code) - ) - sys.stderr.write( - str(resp.json()) - ) - sys.exit(1) - - resp_json = {} - if not DRY_RUN and resp.status_code != 204: - resp_json = resp.json() - - try: - schema = validator_fix_ref(response.get('schema', {}), path) - except: - sys.stderr.write( - 'could not parse response schema for {0}'.format(name) - ) - sys.exit(1) - - if not DRY_RUN: - if not schema: - sys.stderr.write( - 'Warning: no schema to validate response against for {0}\n' - .format(name) - ) - validate(resp_json).against(schema) - - if 'matches' in response: - if 0 != self.equals(response['matches'], resp_json, False) and not DRY_RUN: - sys.stderr.write( - 'Error validating equals for {0}'.format(name) - ) - sys.stderr.write(json.dumps(resp_json, indent=4)) - sys.stderr.write(json.dumps(response['matches'], indent=4)) - sys.exit(1) - - if 'assertRegex' in response: - if 0 != self.equals(response['assertRegex'], resp_json, True) and not DRY_RUN: - sys.stderr.write( - 'Error validating assertRegex for {0}'.format(name) - ) - sys.stderr.write(json.dumps(resp_json, indent=4)) - sys.stderr.write(json.dumps(response['assertRegex'], indent=4)) - sys.exit(1) - - return { - 'response': resp_json, - 'status_code': resp.status_code if not DRY_RUN else 0, - 'request': body - } - - def parse_file(self, name): - scenarios = {} - - data = yaml.load(open(name).read()) - - for other in data.get('require', []): - scenarios.update(**self.parse_file( - find_file(name, other) - )) - - for scenario in data.get('scenarios', []): - scenarios[scenario['name']] = self.run_scenario( - scenario, scenarios, name - ) - - return scenarios - - -def main(): - if len(sys.argv) == 2: - runner = Runner() - runner.cache = json.load(open('fixtures.json')) - result = runner.parse_file(sys.argv[1]) - if DUMP_JSON: - sys.stdout.write(json.dumps(result, indent=1)) - else: - print('python {0} [file of scenario to run]'.format(sys.argv[0])) - - -if __name__ == '__main__': - main() diff --git a/tester/travis.sh b/tester/travis.sh deleted file mode 100755 index 5ec7ad8..0000000 --- a/tester/travis.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -code=0 - -export ROOT_URL="https://api-pmtest.balancedpayments.com" - -./check_json.sh -code=$? - -echo '{}' > fixtures.json - -DRY_RUN=1 ./run_scenarios.sh - -code=$(( $code + $? )) - - -if [ $code -eq 0 ] -then - echo -e "\033[00;32m=================================================\n== ALL SCENARIOS HAVE PASSED THE INITIAL CHECK ==\n== WILL PROCEED TO TEST PUBLIC API ==\n=================================================\033[00m" - - echo "Creating new test marketplace" - ./fixture_data.py > fixtures.json - cat fixtures.json - echo -e "\n\nRunning scenarios against the api" - ./run_scenarios.sh - code=$? - -fi - -exit $code From 0249dcac644194991e8ad58509df1afca8834fd0 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 2 Dec 2013 17:55:08 +0000 Subject: [PATCH 002/154] "Tokenize a card without a secret" works. There's a teeny bit of extra infrastructure to get a token. I built it by accident, but we'll need it eventually, so I just left it in. --- Gemfile | 5 ++++ Gemfile.lock | 29 +++++++++++++++++++ bin/cucumber | 16 +++++++++++ features/cards.feature | 37 +++++++++++++++++++++++++ features/step_definitions/card_steps.rb | 22 +++++++++++++++ features/support/secret.rb | 10 +++++++ 6 files changed, 119 insertions(+) create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100755 bin/cucumber create mode 100644 features/cards.feature create mode 100644 features/step_definitions/card_steps.rb create mode 100644 features/support/secret.rb diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..ae8b63b --- /dev/null +++ b/Gemfile @@ -0,0 +1,5 @@ +source "https://rubygems.org" + +gem "cucumber" +gem "httparty" +gem "json-schema" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..be1dbc1 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,29 @@ +GEM + remote: https://rubygems.org/ + specs: + builder (3.2.2) + cucumber (1.3.10) + builder (>= 2.1.2) + diff-lcs (>= 1.1.3) + gherkin (~> 2.12) + multi_json (>= 1.7.5, < 2.0) + multi_test (>= 0.0.2) + diff-lcs (1.2.5) + gherkin (2.12.2) + multi_json (~> 1.3) + httparty (0.12.0) + json (~> 1.8) + multi_xml (>= 0.5.2) + json (1.8.1) + json-schema (2.1.3) + multi_json (1.8.2) + multi_test (0.0.2) + multi_xml (0.5.5) + +PLATFORMS + ruby + +DEPENDENCIES + cucumber + httparty + json-schema diff --git a/bin/cucumber b/bin/cucumber new file mode 100755 index 0000000..caa0654 --- /dev/null +++ b/bin/cucumber @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# +# This file was generated by Bundler. +# +# The application 'cucumber' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'pathname' +ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", + Pathname.new(__FILE__).realpath) + +require 'rubygems' +require 'bundler/setup' + +load Gem.bin_path('cucumber', 'cucumber') diff --git a/features/cards.feature b/features/cards.feature new file mode 100644 index 0000000..20ae672 --- /dev/null +++ b/features/cards.feature @@ -0,0 +1,37 @@ +Feature: Tokenize a credit card + "Tokenizing" a credit card is the process of sending Balanced a credit card + number for storage. The API will then give the customer back a token that the + customer will store. The idea is that if a customer stored the credit card data + directly, the customer would legally be required to take extra precautions for + safely storing the information, whereas with the token, Balanced worries about + that, and the customer just charges against the token rather than the card. + + For more on tokenization as a concept, see ['tokenization' on + Wikipedia.](http://en.wikipedia.org/wiki/Tokenization_%28data_security%29) + + Scenario: Tokenize a card without a secret key + Cards are able to be tokenized without sending along a secret key. When + this happens, the customer gets less information than if the key was sent. + + When I POST to /cards without my secret key with the JSON API body: + """ + { + "number": "4111111111111111", + "expiration_month": "12", + "expiration_year": 2016 + } + """ + Then I should get a 201 Created status code + And the response has this schema: + """ + { + "cards": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "uniqueItems": true, + "required": true + } + } + """ + diff --git a/features/step_definitions/card_steps.rb b/features/step_definitions/card_steps.rb new file mode 100644 index 0000000..775fcea --- /dev/null +++ b/features/step_definitions/card_steps.rb @@ -0,0 +1,22 @@ +require 'httparty' +require 'json-schema' + +When(/^I POST to (\/.*) without my secret key with the JSON API body:$/) do |url, body| + options = { + headers: { + "Accept" => "application/vnd.api+json;revision=1.1", + }, + body: JSON.parse(body) + } + response = HTTParty.post("https://api.balancedpayments.com#{url}", options) + @response_code = response.code + @response_body = JSON.parse(response.body) +end + +Then(/^I should get a (.+) status code$/) do |code| + assert_equal code.to_i, @response_code +end + +Then(/^the response has this schema:$/) do |schema| + assert JSON::Validator.validate(JSON.parse(schema), @response_body) +end diff --git a/features/support/secret.rb b/features/support/secret.rb new file mode 100644 index 0000000..07331d8 --- /dev/null +++ b/features/support/secret.rb @@ -0,0 +1,10 @@ +require 'httparty' +require 'json' + +options = { + headers: { + "Accept" => "application/vnd.api+json;revision=1.1", + }, +} +response = HTTParty.post("https://api.balancedpayments.com/api_keys", options) +$api_secret = JSON.parse(response.body)["api_keys"][0]["secret"] From 999670a5b67ecdf80105e912d89171b4648cd7b6 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 10 Dec 2013 00:07:24 +0000 Subject: [PATCH 003/154] Set up rake tasks to make running features easier Three things here: 1. I'm going to translate features over, and mark them as failing when they are failing. I'll also tag them with gh-N to mark GitHub issue #N that they go along with. 2. `bin/rake cruise` has been set up to run for CI. Basically, it runs all the things that aren't failing, and makes sure they pass. Then, it runs all thet hings that are supposed to be failing, and makes sure they fail. If either of those are not true, it fails the build, and if they both pass, they pass. This way we know when something starts passing again, and to remove the failing tag. 3. `bin/rake focus` is added when developing new scenarios. You just tag the scenario you're working on with `@focus`, and then `bin/rake focus` will run just that one scenario. --- .travis.yml | 2 +- Gemfile | 1 + Gemfile.lock | 2 + README.md | 31 ++++ Rakefile | 52 ++++++ bin/rake | 16 ++ features/cards.feature | 7 +- features/step_definitions/card_steps.rb | 24 ++- features/support/initial_setup.rb | 27 +++ features/support/secret.rb | 10 -- fixtures/_models/address.json | 54 ++++++ fixtures/_models/card.json | 210 ++++++++++++++++++++++++ fixtures/cards.json | 44 +++++ formatters/in_progress.rb | 60 +++++++ 14 files changed, 526 insertions(+), 14 deletions(-) create mode 100644 Rakefile create mode 100755 bin/rake create mode 100644 features/support/initial_setup.rb delete mode 100644 features/support/secret.rb create mode 100644 fixtures/_models/address.json create mode 100644 fixtures/_models/card.json create mode 100644 fixtures/cards.json create mode 100644 formatters/in_progress.rb diff --git a/.travis.yml b/.travis.yml index 0927861..f973227 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ language: ruby rvm: - 2.0.0 -script: ./bin/cucumber --format=progress +script: bin/rake cruise diff --git a/Gemfile b/Gemfile index ae8b63b..983fc78 100644 --- a/Gemfile +++ b/Gemfile @@ -3,3 +3,4 @@ source "https://rubygems.org" gem "cucumber" gem "httparty" gem "json-schema" +gem "rake" diff --git a/Gemfile.lock b/Gemfile.lock index be1dbc1..4f77649 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -19,6 +19,7 @@ GEM multi_json (1.8.2) multi_test (0.0.2) multi_xml (0.5.5) + rake (10.1.0) PLATFORMS ruby @@ -27,3 +28,4 @@ DEPENDENCIES cucumber httparty json-schema + rake diff --git a/README.md b/README.md index e44df6f..fd43205 100644 --- a/README.md +++ b/README.md @@ -44,3 +44,34 @@ Running Tests $ bundle $ bin/cucumber ``` + +This requires Ruby. We run the changes with Ruby 2.0, but other versions will +probably work as well. [Let us +know](https://github.com/balanced/balanced-api/issues/new) if you have any +problems running these specs, and we'd be happy to help. + +If you're working on a new scenario, the 'focus' task is useful. Tag a scenario +with `@focus`, and then run `bin/rake focus`. It will only run that single scenario. + +### Discussions around changes + +Speaking of issues, that's the second purpose of this repository. If you would +like to see a new feature implemented in the Balanced API, please [open an +issue](https://github.com/balanced/balanced-api/issues/new) and we'll discuss +it. + +For example, one of our biggest requests is to support non-USD currencies. +[Here](https://github.com/balanced/balanced-api/issues/23) is the issue with +the discussion, and when we support this feature, we close the issue via a pull +request that implements the specification, and then everyone on the issue gets +notified. + +Sometimes, Issues are great for collecting feedback, as well. For example, +[all implementation of the current framework was done via +PR](https://github.com/balanced/balanced-api/pull/431), and some issues need +more explanation around use cases by those who want the feature, like [Bitcoin +support](https://github.com/balanced/balanced-api/issues/204). + +We try to do as much 'internal' discussion in these issues as well, it's not +just for public feedback. If you want to know what we're thinking, just search +for a relevant issue! diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..701b883 --- /dev/null +++ b/Rakefile @@ -0,0 +1,52 @@ +require 'cucumber/rake/task' + +class BuildFailure < Exception; + def initialize(message = nil) + message ||= "Build failed" + super(message) + end +end; + +Cucumber::Rake::Task.new do |t| + t.cucumber_opts = "--format progress --tags ~@failing" +end + +Cucumber::Rake::Task.new(:focus) do |t| + t.cucumber_opts = "--format progress --tags @focus" +end + +namespace :features do + desc "Run all features" + Cucumber::Rake::Task.new(:all) do |t| + t.cucumber_opts = "--format progress" + end + + desc "Run in-progress features" + Cucumber::Rake::Task.new(:in_progress) do |t| + t.cucumber_opts = "--require formatters/ --format Cucumber::Formatter::InProgress --tags @failing" + end +end + +desc "Run complete feature build" +task :cruise do + finished_successful = run_and_check_for_exception("cucumber") + in_progress_successful = run_and_check_for_exception("features:in_progress") + + unless finished_successful && in_progress_successful + puts + puts("Finished features had failing steps") unless finished_successful + puts("In-progress Scenario/s passed when they should fail or be pending") unless in_progress_successful + puts + raise BuildFailure + end +end + +def run_and_check_for_exception(task_name) + puts "*** Running #{task_name} features ***" + begin + Rake::Task["#{task_name}"].invoke + rescue Exception => e + return false + end + true +end diff --git a/bin/rake b/bin/rake new file mode 100755 index 0000000..26c7a2d --- /dev/null +++ b/bin/rake @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# +# This file was generated by Bundler. +# +# The application 'rake' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'pathname' +ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", + Pathname.new(__FILE__).realpath) + +require 'rubygems' +require 'bundler/setup' + +load Gem.bin_path('rake', 'rake') diff --git a/features/cards.feature b/features/cards.feature index 20ae672..a2d3234 100644 --- a/features/cards.feature +++ b/features/cards.feature @@ -2,13 +2,13 @@ Feature: Tokenize a credit card "Tokenizing" a credit card is the process of sending Balanced a credit card number for storage. The API will then give the customer back a token that the customer will store. The idea is that if a customer stored the credit card data - directly, the customer would legally be required to take extra precautions for - safely storing the information, whereas with the token, Balanced worries about + directly, the customer would legally be required to take extra precautions for safely storing the information, whereas with the token, Balanced worries about that, and the customer just charges against the token rather than the card. For more on tokenization as a concept, see ['tokenization' on Wikipedia.](http://en.wikipedia.org/wiki/Tokenization_%28data_security%29) + @failing Scenario: Tokenize a card without a secret key Cards are able to be tokenized without sending along a secret key. When this happens, the customer gets less information than if the key was sent. @@ -35,3 +35,6 @@ Feature: Tokenize a credit card } """ + When I GET "cards.href" from the previous response + Then I should get a 200 OK status code + And the response is valid according to the "cards" schema diff --git a/features/step_definitions/card_steps.rb b/features/step_definitions/card_steps.rb index 775fcea..a9a0985 100644 --- a/features/step_definitions/card_steps.rb +++ b/features/step_definitions/card_steps.rb @@ -1,4 +1,3 @@ -require 'httparty' require 'json-schema' When(/^I POST to (\/.*) without my secret key with the JSON API body:$/) do |url, body| @@ -20,3 +19,26 @@ Then(/^the response has this schema:$/) do |schema| assert JSON::Validator.validate(JSON.parse(schema), @response_body) end + +When(/^I GET "(.*?)" from the previous response$/) do |keys| + # hax to access a Ruby hash like dot notation + # Array shennanigans is because we only support the first element + url = keys.split('.').inject(@response_body) {|o, k| Array(o[k])[0] } + options = { + headers: { + "Accept" => "application/vnd.api+json;revision=1.1", + }, + basic_auth: { + username: $api_secret, + password: "", + } + } + + response = HTTParty.get("https://api.balancedpayments.com#{url}", options) + @response_code = response.code + @response_body = JSON.parse(response.body) +end + +Then(/^the response is valid according to the "(.*?)" schema$/) do |filename| + assert JSON::Validator.validate(File.join("fixtures", "#{filename}.json"), @response_body), "The response failed the '#{filename}' schema. Here's the body: #{@response_body}" +end diff --git a/features/support/initial_setup.rb b/features/support/initial_setup.rb new file mode 100644 index 0000000..dbab60c --- /dev/null +++ b/features/support/initial_setup.rb @@ -0,0 +1,27 @@ +require 'httparty' +require 'json' + +# First, we need to create an API key. This is as easy as making a POST request. + +options = { + headers: { + "Accept" => "application/vnd.api+json;revision=1.1", + }, +} +response = HTTParty.post("https://api.balancedpayments.com/api_keys", options) +$api_secret = JSON.parse(response.body)["api_keys"][0]["secret"] + +# Now that we have our key, we need to make a marketplace. Lots of our scenarios +# will fail unless we've made at least one. + +options = { + headers: { + "Accept" => "application/vnd.api+json;revision=1.1", + }, + basic_auth: { + username: $api_secret, + password: "" + }, +} + +HTTParty.post("https://api.balancedpayments.com/marketplaces", options) diff --git a/features/support/secret.rb b/features/support/secret.rb deleted file mode 100644 index 07331d8..0000000 --- a/features/support/secret.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'httparty' -require 'json' - -options = { - headers: { - "Accept" => "application/vnd.api+json;revision=1.1", - }, -} -response = HTTParty.post("https://api.balancedpayments.com/api_keys", options) -$api_secret = JSON.parse(response.body)["api_keys"][0]["secret"] diff --git a/fixtures/_models/address.json b/fixtures/_models/address.json new file mode 100644 index 0000000..e523ff8 --- /dev/null +++ b/fixtures/_models/address.json @@ -0,0 +1,54 @@ +{ + "$schema": "http://json-schema.org/draft-03/schema#", + "type": "object", + "properties": { + "line1": { + "type": [ + "string", + "null" + ], + "required": true + }, + "line2": { + "type": [ + "string", + "null" + ], + "required": true + }, + "city": { + "type": [ + "string", + "null" + ], + "required": true + }, + "state": { + "type": [ + "string", + "null" + ], + "required": true + }, + "postal_code": { + "type": [ + "string", + "null" + ], + "required": true + }, + "country_code": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "string", + "pattern": "[A-Z]{2}" + } + ], + "required": true + } + }, + "additionalProperties": false +} diff --git a/fixtures/_models/card.json b/fixtures/_models/card.json new file mode 100644 index 0000000..c78b0d8 --- /dev/null +++ b/fixtures/_models/card.json @@ -0,0 +1,210 @@ +{ + "$schema": "http://json-schema.org/draft-03/schema#", + "type": "object", + "properties": { + "id": { + "type": "string", + "required": true, + "pattern": "CC[a-zA-Z0-9]{16,32}" + }, + "href": { + "type": "string", + "format": "uri", + "required": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "required": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "required": true + }, + "name": { + "type": [ + "string", + "null" + ], + "required": true + }, + "number": { + "type": "string", + "pattern": "x{11,12}[0-9]{4}", + "required": true + }, + "expiration_month": { + "type": "integer", + "minimum": 1, + "maximum": 12, + "required": true + }, + "expiration_year": { + "type": "integer", + "required": true + }, + "cvv": { + "anyOf": [ + { + "type": "string", + "pattern": "x{3,4}" + }, + { + "type": "null" + } + ], + "required": true + }, + "cvv_match": { + "anyOf": [ + { + "type": "string", + "enum": [ + "yes", + "no", + "unsupported" + ] + }, + { + "type": "null" + } + ], + "required": true + }, + "cvv_result": { + "anyOf": [ + { + "type": "string", + "enum": [ + "Suspicious transaction", + "Failed data validation check", + "Match", + "No Match", + "Not Processed", + "Should have been present", + "Issuer unable to process request", + "Card does not support verification" + ] + }, + { + "type": "null" + } + ], + "required": true + }, + "address": { + "$ref": "address.json", + "required": true + }, + "avs_street_match": { + "anyOf": [ + { + "type": "string", + "enum": [ + "yes", + "no", + "unsupported" + ] + }, + { + "type": "null" + } + ], + "required": true + }, + "avs_postal_match": { + "anyOf": [ + { + "type": "string", + "enum": [ + "yes", + "no", + "unsupported" + ] + }, + { + "type": "null" + } + ], + "required": true + }, + "avs_result": { + "anyOf": [ + { + "type": "string", + "enum": [ + "Street address matches, but 5-digit and 9-digit postal code do not match.", + "Street address matches, but postal code not verified.", + "Street address and postal code do not match.", + "Street address and postal code match.", + "AVS data is invalid or AVS is not allowed for this card type.", + "Card member's name does not match, but billing postal code matches.", + "Non-U.S. issuing bank does not support AVS.", + "Card member's name does not match. Street address and postal code match.", + "Address not verified.", + "Card member's name, billing address, and postal code match. Shipping information verified and chargeback protection guaranteed through the Fraud Protection Program.", + "Card member's name matches but billing address and billing postal code do not match.", + "Card member's name and billing postal code match, but billing address does not match.", + "Street address and postal code match.", + "Street address and postal code do not match.", + "Card member's name and billing address match, but billing postal code does not match.", + "Postal code matches, but street address not verified.", + "Card member's name, billing address, and postal code match. Shipping information verified but chargeback protection not guaranteed.", + "System unavailable.", + "U.S.-issuing bank does not support AVS.", + "Card member's name does not match, but street address matches.", + "Address information unavailable.", + "Card member's name, billing address, and billing postal code match.", + "Street address does not match, but 9-digit postal code matches.", + "Street address and 9-digit postal code match.", + "Street address and 5-digit postal code match.", + "Street address does not match, but 5-digit postal code matches." + ] + }, + { + "type": "null" + } + ], + "required": true + }, + "fingerprint": { + "type": "string", + "required": true + }, + "brand": { + "type": "string", + "enum": [ + "Visa", + "MasterCard", + "Discover", + "American Express" + ], + "required": false + }, + "is_verified": { + "type": "boolean", + "required": true + }, + "meta": { + "type": "object", + "required": true + }, + "links": { + "type": "object", + "properties": { + "customer": { + "type": [ + "null", + "string" + ], + "pattern": "(CU|AC)[a-zA-Z0-9]{16,32}", + "required": true + } + }, + "required": true, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/fixtures/cards.json b/fixtures/cards.json new file mode 100644 index 0000000..84e6d9f --- /dev/null +++ b/fixtures/cards.json @@ -0,0 +1,44 @@ +{ + "type": "object", + "properties": { + "links": { + "type": "object", + "properties": { + "cards.card_holds": { + "type": "string", + "format": "uri", + "pattern": "/cards/{cards.id}/card_holds" + }, + "cards.debits": { + "type": "string", + "format": "uri", + "pattern": "/cards/{cards.id}/debits" + }, + "cards.customer": { + "type": "string", + "format": "uri", + "pattern": "/customers/{cards.customer}" + } + }, + "required": [ + "cards.customer", + "cards.debits", + "cards.card_holds" + ] + }, + "meta": { + "type": "object" + }, + "cards": { + "items": { + "$ref": "_models/card.json" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true + } + }, + "required": [ + "cards" + ] +} diff --git a/formatters/in_progress.rb b/formatters/in_progress.rb new file mode 100644 index 0000000..4a24a92 --- /dev/null +++ b/formatters/in_progress.rb @@ -0,0 +1,60 @@ +require 'cucumber/formatter/progress' + +module Cucumber + module Formatter + class InProgress < Progress + FAILURE_CODE = 1 + SUCCESS_CODE = 0 + + FORMATS[:invalid_pass] = Proc.new{ |string| ::Term::ANSIColor.blue(string) } + + def initialize(step_mother, io, options) + super(step_mother, io, options) + @scenario_passed = true + @passing_scenarios = [] + @feature_element_count = 0 + end + + def visit_feature_element(feature_element) + super + + @passing_scenarios << feature_element if @scenario_passed + @scenario_passed = true + @feature_element_count += 1 + + @io.flush + end + + def visit_exception(exception, status) + @scenario_passed = false + super + end + + private + + def print_summary(features) + unless @passing_scenarios.empty? + @io.puts format_string("(::) Scenarios passing which should be failing or pending (::)", :invalid_pass) + @io.puts + @passing_scenarios.each do |element| + @io.puts(format_string(element.backtrace_line, :invalid_pass)) + end + @io.puts + end + + unless @passing_scenarios.empty? + override_exit_code(FAILURE_CODE) + else + override_exit_code(SUCCESS_CODE) + end + end + + def override_exit_code(status_code) + at_exit do + Kernel.exit(status_code) + end + end + + end + end +end From e48738e8ef630676a7436a8f1303aa56492e59dc Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Thu, 12 Dec 2013 22:48:19 +0000 Subject: [PATCH 004/154] New feature: creating API keys. There was no existing schemas for the API key response, so I made one. --- features/api_keys.feature | 32 +++++++++++++++++++++++++ features/step_definitions/card_steps.rb | 11 +++++++++ 2 files changed, 43 insertions(+) create mode 100644 features/api_keys.feature diff --git a/features/api_keys.feature b/features/api_keys.feature new file mode 100644 index 0000000..c5644d7 --- /dev/null +++ b/features/api_keys.feature @@ -0,0 +1,32 @@ +Feature: API Keys + API Keys are what customers use to authenticate against the Balanced API. + Generally speaking, this will be the first step that is needed to be taken by + the customer to get started with the API. + + API keys are used to make authenticated requests by sending an HTTP Basic + Auth header, using the key as the username, with no password. + + Scenario: Create an API Key + To obtain a key, one must be created. This is done through an + unauthenticated API request. + + When I POST to /api_keys without my secret key + Then I should get a 201 Created status code + And the response has this schema: + """ + { + "api_keys": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "properties": { + "href": { "type": "string" }, + "id": { "type": "string" }, + "secret": { "type": "string" }, + "links": { "type": "object" }, + "meta": { "type": "object" } + } + }, + "required": ["api_keys"] + } + """ diff --git a/features/step_definitions/card_steps.rb b/features/step_definitions/card_steps.rb index a9a0985..b158be7 100644 --- a/features/step_definitions/card_steps.rb +++ b/features/step_definitions/card_steps.rb @@ -12,6 +12,17 @@ @response_body = JSON.parse(response.body) end +When(/^I POST to (\/.*) without my secret key$/) do |url| + options = { + headers: { + "Accept" => "application/vnd.api+json;revision=1.1", + }, + } + response = HTTParty.post("https://api.balancedpayments.com#{url}", options) + @response_code = response.code + @response_body = JSON.parse(response.body) +end + Then(/^I should get a (.+) status code$/) do |code| assert_equal code.to_i, @response_code end From e494c510e9262e30b4f544e023222d37bb765383 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Thu, 12 Dec 2013 23:32:59 +0000 Subject: [PATCH 005/154] New feature: retrieve API keys --- features/api_keys.feature | 26 ++++++++++++++++++++++++++ features/step_definitions/api_keys.rb | 27 +++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 features/step_definitions/api_keys.rb diff --git a/features/api_keys.feature b/features/api_keys.feature index c5644d7..3a03201 100644 --- a/features/api_keys.feature +++ b/features/api_keys.feature @@ -30,3 +30,29 @@ Feature: API Keys "required": ["api_keys"] } """ + + Scenario: Retrieve information about an existing API key + Right now, there's not a whole lot of extra information, but we support it + anyway. + + Given I have created an API key + When I GET to /api_keys/:api_key giving the key + Then I should get a 200 OK status code + And the response has this schema: + """ + { + "api_keys": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "properties": { + "href": { "type": "string" }, + "id": { "type": "string" }, + "secret": { "type": "string" }, + "links": { "type": "object" }, + "meta": { "type": "object" } + } + }, + "required": ["api_keys"] + } + """ diff --git a/features/step_definitions/api_keys.rb b/features/step_definitions/api_keys.rb new file mode 100644 index 0000000..b71b202 --- /dev/null +++ b/features/step_definitions/api_keys.rb @@ -0,0 +1,27 @@ +Given(/^I have created an API key$/) do + options = { + headers: { + "Accept" => "application/vnd.api+json;revision=1.1", + }, + } + response = HTTParty.post("https://api.balancedpayments.com/api_keys", options) + @response_code = response.code + @response_body = JSON.parse(response.body) + @api_secret = @response_body["api_keys"][0]["secret"] # ugh + @api_key = @response_body["api_keys"][0]["href"].gsub("/api_keys/", "") # lol +end + +When(/^I GET to \/api_keys\/:api_key giving the key$/) do + options = { + headers: { + "Accept" => "application/vnd.api+json;revision=1.1", + }, + basic_auth: { + username: @api_secret, + password: "", + } + } + response = HTTParty.get("https://api.balancedpayments.com/api_keys/#{@api_key}", options) + @response_code = response.code + @response_body = JSON.parse(response.body) +end From 82780ea93eb552b3aa76aa3609ddf597bd490999 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 13 Dec 2013 00:06:12 +0000 Subject: [PATCH 006/154] New feature: list all API keys --- features/api_keys.feature | 41 +++++++++++++++++++++++++ features/step_definitions/api_keys.rb | 19 ++++++++++++ features/step_definitions/card_steps.rb | 2 +- 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/features/api_keys.feature b/features/api_keys.feature index 3a03201..3d770c5 100644 --- a/features/api_keys.feature +++ b/features/api_keys.feature @@ -56,3 +56,44 @@ Feature: API Keys "required": ["api_keys"] } """ + + Scenario: List all API keys + Customers can make as many keys as they'd like. Being able to see all of + them is a good thing. + + Given I have created more than one API keys + When I GET to /api_keys + Then I should get a 200 OK status code + And the response has this schema: + """ + { + "api_keys": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "properties": { + "href": { "type": "string" }, + "id": { "type": "string" }, + "secret": { "type": "string" }, + "links": { "type": "object" }, + "meta": { "type": "object" } + } + }, + "properties": { + "meta": { + "type": "object", + "properties": { + "first": { "type": "string" }, + "href": { "type": "string" }, + "last": { "type": "string" }, + "limit": { "type": "integer" }, + "next": { "type": ["string", "null"] }, + "offset": { "type": "integer" }, + "previous": { "type": ["string", "null"] }, + "total": { "type": "integer" } + } + } + }, + "required": ["api_keys"] + } + """ diff --git a/features/step_definitions/api_keys.rb b/features/step_definitions/api_keys.rb index b71b202..d40ad9e 100644 --- a/features/step_definitions/api_keys.rb +++ b/features/step_definitions/api_keys.rb @@ -25,3 +25,22 @@ @response_code = response.code @response_body = JSON.parse(response.body) end + +Given(/^I have created more than one API keys$/) do + 2.times { step "I have created an API key" } +end + +When(/^I GET to \/api_keys$/) do + options = { + headers: { + "Accept" => "application/vnd.api+json;revision=1.1", + }, + basic_auth: { + username: @api_secret, + password: "", + } + } + response = HTTParty.get("https://api.balancedpayments.com/api_keys", options) + @response_code = response.code + @response_body = JSON.parse(response.body) +end diff --git a/features/step_definitions/card_steps.rb b/features/step_definitions/card_steps.rb index b158be7..9a9102e 100644 --- a/features/step_definitions/card_steps.rb +++ b/features/step_definitions/card_steps.rb @@ -28,7 +28,7 @@ end Then(/^the response has this schema:$/) do |schema| - assert JSON::Validator.validate(JSON.parse(schema), @response_body) + assert JSON::Validator.validate!(JSON.parse(schema), @response_body) end When(/^I GET "(.*?)" from the previous response$/) do |keys| From c3e3f5b086e7ed504934f317b744990be74cdb50 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 13 Dec 2013 00:38:12 +0000 Subject: [PATCH 007/154] New feature: remove API keys --- features/api_keys.feature | 9 +++++++++ features/step_definitions/api_keys.rb | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/features/api_keys.feature b/features/api_keys.feature index 3d770c5..c1bd7c2 100644 --- a/features/api_keys.feature +++ b/features/api_keys.feature @@ -97,3 +97,12 @@ Feature: API Keys "required": ["api_keys"] } """ + + Scenario: Remove an API key + Sometimes, a customer just doesn't want an API key any more. Deleting that + key will get rid of it. + + Given I have created an API key + When I DELETE to /api_keys/:api_key giving the key + Then I should get a 204 OK status code + And there should be no response body diff --git a/features/step_definitions/api_keys.rb b/features/step_definitions/api_keys.rb index d40ad9e..f7d1588 100644 --- a/features/step_definitions/api_keys.rb +++ b/features/step_definitions/api_keys.rb @@ -26,6 +26,21 @@ @response_body = JSON.parse(response.body) end +When(/^I DELETE to \/api_keys\/:api_key giving the key$/) do + options = { + headers: { + "Accept" => "application/vnd.api+json;revision=1.1", + }, + basic_auth: { + username: @api_secret, + password: "", + } + } + response = HTTParty.delete("https://api.balancedpayments.com/api_keys/#{@api_key}", options) + @response_code = response.code + @response_body = response.body +end + Given(/^I have created more than one API keys$/) do 2.times { step "I have created an API key" } end @@ -44,3 +59,7 @@ @response_code = response.code @response_body = JSON.parse(response.body) end + +Then(/^there should be no response body$/) do + assert_nil @response_body +end From 7a8e23a2fda2e4d03c8dc1a2ebd88273d7d8a4eb Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 13 Dec 2013 01:06:14 +0000 Subject: [PATCH 008/154] Refactor/reorganize steps for clarity. It's important not to refactor steps _too_ much, but these were certainly http steps and not card steps. --- features/step_definitions/api_keys.rb | 33 ++++------------- .../{card_steps.rb => http_steps.rb} | 37 ++++++++++++++----- 2 files changed, 35 insertions(+), 35 deletions(-) rename features/step_definitions/{card_steps.rb => http_steps.rb} (79%) diff --git a/features/step_definitions/api_keys.rb b/features/step_definitions/api_keys.rb index f7d1588..c23d84a 100644 --- a/features/step_definitions/api_keys.rb +++ b/features/step_definitions/api_keys.rb @@ -1,4 +1,4 @@ -Given(/^I have created an API key$/) do +Given(/^I have created an API key$/) do options = { headers: { "Accept" => "application/vnd.api+json;revision=1.1", @@ -9,9 +9,9 @@ @response_body = JSON.parse(response.body) @api_secret = @response_body["api_keys"][0]["secret"] # ugh @api_key = @response_body["api_keys"][0]["href"].gsub("/api_keys/", "") # lol -end - -When(/^I GET to \/api_keys\/:api_key giving the key$/) do +end + +When(/^I GET to \/api_keys\/:api_key giving the key$/) do options = { headers: { "Accept" => "application/vnd.api+json;revision=1.1", @@ -24,9 +24,9 @@ response = HTTParty.get("https://api.balancedpayments.com/api_keys/#{@api_key}", options) @response_code = response.code @response_body = JSON.parse(response.body) -end +end -When(/^I DELETE to \/api_keys\/:api_key giving the key$/) do +When(/^I DELETE to \/api_keys\/:api_key giving the key$/) do options = { headers: { "Accept" => "application/vnd.api+json;revision=1.1", @@ -39,27 +39,8 @@ response = HTTParty.delete("https://api.balancedpayments.com/api_keys/#{@api_key}", options) @response_code = response.code @response_body = response.body -end +end Given(/^I have created more than one API keys$/) do 2.times { step "I have created an API key" } -end - -When(/^I GET to \/api_keys$/) do - options = { - headers: { - "Accept" => "application/vnd.api+json;revision=1.1", - }, - basic_auth: { - username: @api_secret, - password: "", - } - } - response = HTTParty.get("https://api.balancedpayments.com/api_keys", options) - @response_code = response.code - @response_body = JSON.parse(response.body) -end - -Then(/^there should be no response body$/) do - assert_nil @response_body end diff --git a/features/step_definitions/card_steps.rb b/features/step_definitions/http_steps.rb similarity index 79% rename from features/step_definitions/card_steps.rb rename to features/step_definitions/http_steps.rb index 9a9102e..ef6adb7 100644 --- a/features/step_definitions/card_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -1,4 +1,17 @@ -require 'json-schema' +When(/^I (\w+) to (\/\S*?)$/) do |verb, url| + options = { + headers: { + "Accept" => "application/vnd.api+json;revision=1.1", + }, + basic_auth: { + username: @api_secret, + password: "", + } + } + response = HTTParty.send(verb.downcase, "https://api.balancedpayments.com#{url}", options) + @response_code = response.code + @response_body = JSON.parse(response.body) +end When(/^I POST to (\/.*) without my secret key with the JSON API body:$/) do |url, body| options = { @@ -23,14 +36,6 @@ @response_body = JSON.parse(response.body) end -Then(/^I should get a (.+) status code$/) do |code| - assert_equal code.to_i, @response_code -end - -Then(/^the response has this schema:$/) do |schema| - assert JSON::Validator.validate!(JSON.parse(schema), @response_body) -end - When(/^I GET "(.*?)" from the previous response$/) do |keys| # hax to access a Ruby hash like dot notation # Array shennanigans is because we only support the first element @@ -50,6 +55,20 @@ @response_body = JSON.parse(response.body) end +require 'json-schema' +Then(/^the response has this schema:$/) do |schema| + assert JSON::Validator.validate!(JSON.parse(schema), @response_body) +end + Then(/^the response is valid according to the "(.*?)" schema$/) do |filename| assert JSON::Validator.validate(File.join("fixtures", "#{filename}.json"), @response_body), "The response failed the '#{filename}' schema. Here's the body: #{@response_body}" end + +Then(/^I should get a (.+) status code$/) do |code| + assert_equal code.to_i, @response_code +end + +Then(/^there should be no response body$/) do + assert_nil @response_body +end + From 65a81b899b09b4638e21c17c6fd4dad8b7c8b955 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 13 Dec 2013 18:28:05 +0000 Subject: [PATCH 009/154] Update README. --- README.md | 54 +++++++++++++++--------------------------------------- 1 file changed, 15 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index fd43205..7c4da23 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,24 @@ -[![Build Status](https://travis-ci.org/balanced/balanced-api.png?branch=revision1)](https://travis-ci.org/balanced/balanced-api) - -Balanced API Spec -================= - -Online marketplaces enable a new form of commerce at a scale that has never -existed before. The success of online marketplaces has the potential to -materially effect the global economy by creating new jobs and economic activity -that previously did not exist. Airbnb, Kickstarter, Etsy, and other -marketplaces have created new forms of income for businesses and individuals. -That's what gets the [Balanced](https://www.balancedpayments.com/) team excited -and what the [Balanced API](https://www.balancedpayments.com/docs/integration) -hopes to support. - -Payments for marketplaces is unfortunately painful because of unique -requirements including paying sellers, payments aggregation policies, tax -ramifications, and fraud. Balanced lets you charge cards, escrow funds, and pay -sellers the next business day without the pain of building a payments system. +## Balanced API Specification -The primary goal of this repo is to create more openness behind the decisions -driving the designs and functionality of the Balanced API. We reached out to -existing and potential customers when designing the API, but that was a limited -set of people we already knew. We've received tremendous growth in the last few -months, and our new customers have great feedback or at least want to -understand the reasoning behind the original decisions. - -We're going to automate validation of our API code against the specifications -in the repo. Any changes to the API can't be deployed unless they've been -merged into master first. Any merge to master will happen concurrently with -deploys of the API and the [docs](https://www.balancedpayments.com/docs). That -means the specs need to be auto generated (except for comments) instead of -handwritten to make sure the code and specs can never go out of sync. +[![Build Status](https://travis-ci.org/balanced/balanced-api.png?branch=revision1)](https://travis-ci.org/balanced/balanced-api) -We'll do our best to have even internal discussions online. All changes (even -by the Balanced team) to the specs must be submitted via pull request and can -only be merged in by @matin after giving the community a chance to comment on -the changes. +This repository contains an executable specification of the Balanced API. The +goal of this repository is two-fold. First, to ensure that the API is working +according to the spec. Second, to provide a place to have discussions around +the API as a product. +### An Executable Specification -Running Tests -============= +Specifications don't matter if they're not followed. Therefore, this +specification is executable, so we can ensure conformance to the spec. There are +more details in the README file inside the `features` directory, but to run these +tests yourself, simply: -``` +```bash +$ git clone https://github.com/balanced/balanced-api.git +$ cd balanced-api $ bundle -$ bin/cucumber +$ bin/rake cucumber ``` This requires Ruby. We run the changes with Ruby 2.0, but other versions will From 424c741aea264822df9bb124171d69a6af9673b0 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 13 Dec 2013 19:32:48 +0000 Subject: [PATCH 010/154] Adding technical documentation. --- features/README.md | 81 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 features/README.md diff --git a/features/README.md b/features/README.md new file mode 100644 index 0000000..91a554b --- /dev/null +++ b/features/README.md @@ -0,0 +1,81 @@ +## Balanced API Spec: Technical details + +Here's where the rubber hits the road. + +### Background + +We use [Cucumber](http://cukes.info/) for features. What that means is that the +descriptions are in psuedo-English, which then gets mapped to code. + +You can check out the specifications by looking at any of the files that end +with `.feature` in this directory. They look like this: + +``` +Feature: API Keys + API Keys are what customers use to authenticate against the Balanced API. + Generally speaking, this will be the first step that is needed to be taken by + the customer to get started with the API. + + Scenario: List all API keys + Customers can make as many keys as they'd like. Being able to see all of + them is a good thing. + + Given I have created more than one API keys + When I GET to /api_keys + Then I should get a 200 OK status code +``` + +As you can see by the indentation, each file describes a 'feature.' There's +then some descriptive text about what that feature is and why it's important. +Features are a collection of 'scenarios,' which describe different things the +API can do. These scenarios also have some descriptive text as well. Scenarios +are made up of 'steps,' which start with "Given," "When," "Then," "And," and a +few others. These steps are actually executed by Cucumber: they run actual code. + +That code is determined by the `step\_definitions` files, which are in that +directory. They map these feature lines to actual Ruby code. For example, + +``` +When I GET to /api_keys +``` + +is mapped to + +``` +When(/^I (\w+) to (\/\S*?)$/) do |verb, url| + options = { + headers: { + "Accept" => "application/vnd.api+json;revision=1.1", + }, + basic_auth: { + username: $api_secret, + password: "", + } + } + response = HTTParty.send(verb.downcase, "https://api.balancedpayments.com#{url}", options) + @response_code = response.code + @response_body = JSON.parse(response.body) +end +``` + +This is illustrative of why we're using Cucumber; if you're not a Rubyist, you +can probably follow the second bit, but it's full of details you just don't +care about. You care that you're making a GET to a certain URL, the rest of the stuff +is just noise. + +There is also a `support` directory with some background code that's used to +set things up. You probably don't care about it. + +### Running the tests + +The tests are run via `rake`. Simply go to the root level of the repository, and run + +``` +$ bin/rake cucumber +``` + +to run the specs that are currently passing. To see all the tasks, + +``` +$ bin/rake -T +``` From 83973909c06799b6bfb1cb9c66a5544bcc653cf4 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Fri, 13 Dec 2013 14:12:14 -0800 Subject: [PATCH 011/154] moving root_url and accept_headers into global variables --- features/cards.feature | 1 + features/step_definitions/api_keys.rb | 14 +++++++------- features/step_definitions/http_steps.rb | 17 ++++++++--------- features/support/initial_setup.rb | 11 +++++++---- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/features/cards.feature b/features/cards.feature index a2d3234..10cde27 100644 --- a/features/cards.feature +++ b/features/cards.feature @@ -38,3 +38,4 @@ Feature: Tokenize a credit card When I GET "cards.href" from the previous response Then I should get a 200 OK status code And the response is valid according to the "cards" schema + And the response isasdf valid according to the "cards" schema diff --git a/features/step_definitions/api_keys.rb b/features/step_definitions/api_keys.rb index c23d84a..4609594 100644 --- a/features/step_definitions/api_keys.rb +++ b/features/step_definitions/api_keys.rb @@ -1,27 +1,27 @@ Given(/^I have created an API key$/) do options = { headers: { - "Accept" => "application/vnd.api+json;revision=1.1", + "Accept" => $accept_header, }, } - response = HTTParty.post("https://api.balancedpayments.com/api_keys", options) + response = HTTParty.post("#{$root_url}/api_keys", options) @response_code = response.code @response_body = JSON.parse(response.body) @api_secret = @response_body["api_keys"][0]["secret"] # ugh - @api_key = @response_body["api_keys"][0]["href"].gsub("/api_keys/", "") # lol + @api_key = @response_body["api_keys"][0]["id"] end When(/^I GET to \/api_keys\/:api_key giving the key$/) do options = { headers: { - "Accept" => "application/vnd.api+json;revision=1.1", + "Accept" => $accept_header, }, basic_auth: { username: @api_secret, password: "", } } - response = HTTParty.get("https://api.balancedpayments.com/api_keys/#{@api_key}", options) + response = HTTParty.get("#{$root_url}/api_keys/#{@api_key}", options) @response_code = response.code @response_body = JSON.parse(response.body) end @@ -29,14 +29,14 @@ When(/^I DELETE to \/api_keys\/:api_key giving the key$/) do options = { headers: { - "Accept" => "application/vnd.api+json;revision=1.1", + "Accept" => $accept_header, }, basic_auth: { username: @api_secret, password: "", } } - response = HTTParty.delete("https://api.balancedpayments.com/api_keys/#{@api_key}", options) + response = HTTParty.delete("#{$root_url}/api_keys/#{@api_key}", options) @response_code = response.code @response_body = response.body end diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index ef6adb7..99becca 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -1,14 +1,14 @@ When(/^I (\w+) to (\/\S*?)$/) do |verb, url| options = { headers: { - "Accept" => "application/vnd.api+json;revision=1.1", + "Accept" => $accept_header, }, basic_auth: { username: @api_secret, password: "", } } - response = HTTParty.send(verb.downcase, "https://api.balancedpayments.com#{url}", options) + response = HTTParty.send(verb.downcase, "#{$root_url}#{url}", options) @response_code = response.code @response_body = JSON.parse(response.body) end @@ -16,11 +16,11 @@ When(/^I POST to (\/.*) without my secret key with the JSON API body:$/) do |url, body| options = { headers: { - "Accept" => "application/vnd.api+json;revision=1.1", + "Accept" => $accept_header, }, body: JSON.parse(body) } - response = HTTParty.post("https://api.balancedpayments.com#{url}", options) + response = HTTParty.post("#{$root_url}#{url}", options) @response_code = response.code @response_body = JSON.parse(response.body) end @@ -28,10 +28,10 @@ When(/^I POST to (\/.*) without my secret key$/) do |url| options = { headers: { - "Accept" => "application/vnd.api+json;revision=1.1", + "Accept" => $accept_header, }, } - response = HTTParty.post("https://api.balancedpayments.com#{url}", options) + response = HTTParty.post("#{$root_url}#{url}", options) @response_code = response.code @response_body = JSON.parse(response.body) end @@ -42,7 +42,7 @@ url = keys.split('.').inject(@response_body) {|o, k| Array(o[k])[0] } options = { headers: { - "Accept" => "application/vnd.api+json;revision=1.1", + "Accept" => $accept_header, }, basic_auth: { username: $api_secret, @@ -50,7 +50,7 @@ } } - response = HTTParty.get("https://api.balancedpayments.com#{url}", options) + response = HTTParty.get("#{$root_url}#{url}", options) @response_code = response.code @response_body = JSON.parse(response.body) end @@ -71,4 +71,3 @@ Then(/^there should be no response body$/) do assert_nil @response_body end - diff --git a/features/support/initial_setup.rb b/features/support/initial_setup.rb index dbab60c..637a88d 100644 --- a/features/support/initial_setup.rb +++ b/features/support/initial_setup.rb @@ -1,14 +1,17 @@ require 'httparty' require 'json' +$root_url = 'https://api.balancedpayments.com' +$accept_header = 'application/vnd.api+json;revision=1.1' + # First, we need to create an API key. This is as easy as making a POST request. options = { headers: { - "Accept" => "application/vnd.api+json;revision=1.1", + "Accept" => $accept_header, }, } -response = HTTParty.post("https://api.balancedpayments.com/api_keys", options) +response = HTTParty.post("#{$root_url}/api_keys", options) $api_secret = JSON.parse(response.body)["api_keys"][0]["secret"] # Now that we have our key, we need to make a marketplace. Lots of our scenarios @@ -16,7 +19,7 @@ options = { headers: { - "Accept" => "application/vnd.api+json;revision=1.1", + "Accept" => $accept_header, }, basic_auth: { username: $api_secret, @@ -24,4 +27,4 @@ }, } -HTTParty.post("https://api.balancedpayments.com/marketplaces", options) +HTTParty.post("#{$root_url}/marketplaces", options) From 6f283338d3c0880090303292212b8d3659263585 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Fri, 13 Dec 2013 14:30:32 -0800 Subject: [PATCH 012/154] adding card tokens schema --- features/cards.feature | 18 +----------------- fixtures/card_tokens.json | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 17 deletions(-) create mode 100644 fixtures/card_tokens.json diff --git a/features/cards.feature b/features/cards.feature index 10cde27..65df17e 100644 --- a/features/cards.feature +++ b/features/cards.feature @@ -22,20 +22,4 @@ Feature: Tokenize a credit card } """ Then I should get a 201 Created status code - And the response has this schema: - """ - { - "cards": { - "type": "array", - "minItems": 1, - "maxItems": 1, - "uniqueItems": true, - "required": true - } - } - """ - - When I GET "cards.href" from the previous response - Then I should get a 200 OK status code - And the response is valid according to the "cards" schema - And the response isasdf valid according to the "cards" schema + And the response is valid according to the "card_tokens" schema diff --git a/fixtures/card_tokens.json b/fixtures/card_tokens.json new file mode 100644 index 0000000..7712d25 --- /dev/null +++ b/fixtures/card_tokens.json @@ -0,0 +1,26 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Card tokenization response", + "description": "When using balanced.js or POSTing without an API key to create a Card, this is the schema that is followed instead of the cards.json or bank_accounts.json schema.", + "type": "object", + "properties": { + "links": { + "type": "object", + "required": false + }, + "meta": { + "type": "object", + "required": false + }, + "cards": { + "items": { + "$ref": "_models/token.json" + }, + "type": "array", + "minItems": 1, + "maxItems": 1, + "uniqueItems": true, + "required": true + } + } +} \ No newline at end of file From f4328d5142d4820139348851301b4349709b10e4 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Fri, 13 Dec 2013 15:54:01 -0800 Subject: [PATCH 013/154] Refactor schema error handling, reintroduce failure We should handle these errors better, and now that we are, let's reintroduce the failing second half of the scenario. I'm actually not entirely sure that flunk-ing with the message is any different than just bubbling up the error, but at least it's a real assertion failure error, rather than a JSON API parsing error. --- features/cards.feature | 4 ++++ features/step_definitions/http_steps.rb | 13 +++++++++++-- fixtures/_models/token.json | 14 ++++++++------ fixtures/card_tokens.json | 16 ++++++++-------- 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/features/cards.feature b/features/cards.feature index 65df17e..56d55e3 100644 --- a/features/cards.feature +++ b/features/cards.feature @@ -23,3 +23,7 @@ Feature: Tokenize a credit card """ Then I should get a 201 Created status code And the response is valid according to the "card_tokens" schema + + When I GET "cards.href" from the previous response + Then I should get a 200 OK status code + And the response is valid according to the "cards" schema diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 99becca..24e7577 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -57,11 +57,20 @@ require 'json-schema' Then(/^the response has this schema:$/) do |schema| - assert JSON::Validator.validate!(JSON.parse(schema), @response_body) + begin + JSON::Validator.validate!(JSON.parse(schema), @response_body) + rescue JSON::Schema::ValidationError => e + flunk(e.message) + end end Then(/^the response is valid according to the "(.*?)" schema$/) do |filename| - assert JSON::Validator.validate(File.join("fixtures", "#{filename}.json"), @response_body), "The response failed the '#{filename}' schema. Here's the body: #{@response_body}" + file_name = File.join('fixtures', "#{filename}.json") + begin + JSON::Validator.validate!(file_name, @response_body) + rescue JSON::Schema::ValidationError => e + flunk(e.message) + end end Then(/^I should get a (.+) status code$/) do |code| diff --git a/fixtures/_models/token.json b/fixtures/_models/token.json index 6ac6513..17d76ba 100644 --- a/fixtures/_models/token.json +++ b/fixtures/_models/token.json @@ -4,20 +4,22 @@ "properties": { "id": { "type": "string", - "required": true, "pattern": "(CC|BA)[a-zA-Z0-9]{16,32}" }, "href": { "type": "string", - "format": "uri", - "required": true + "format": "uri" }, "links": { "type": "object", "properties": {}, - "additionalProperties": false, - "required": true + "additionalProperties": false } }, + "required": [ + "id", + "href", + "links" + ], "additionalProperties": false -} \ No newline at end of file +} diff --git a/fixtures/card_tokens.json b/fixtures/card_tokens.json index 7712d25..4eb01a5 100644 --- a/fixtures/card_tokens.json +++ b/fixtures/card_tokens.json @@ -5,12 +5,10 @@ "type": "object", "properties": { "links": { - "type": "object", - "required": false + "type": "object" }, "meta": { - "type": "object", - "required": false + "type": "object" }, "cards": { "items": { @@ -19,8 +17,10 @@ "type": "array", "minItems": 1, "maxItems": 1, - "uniqueItems": true, - "required": true + "uniqueItems": true } - } -} \ No newline at end of file + }, + "required": [ + "cards" + ] +} From 224b089be19a1f4aff0f5d933948b5c67bd32c63 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Fri, 13 Dec 2013 16:34:06 -0800 Subject: [PATCH 014/154] basic create customer scenario --- features/customers.feature | 23 +++++++ features/step_definitions/http_steps.rb | 59 ++++++++++++++---- fixtures/_models/customer.json | 78 ++++++++++++----------- fixtures/customers.json | 82 +++++++++++++++++++++++++ 4 files changed, 193 insertions(+), 49 deletions(-) create mode 100644 features/customers.feature create mode 100644 fixtures/customers.json diff --git a/features/customers.feature b/features/customers.feature new file mode 100644 index 0000000..eb88018 --- /dev/null +++ b/features/customers.feature @@ -0,0 +1,23 @@ +Feature: Customers + Customers are used for representing both buyers on a marketplace. + Customers have cards and bank accounts associated to themselves. + All debit and credits can be tracked for a specific customer. + + Scenario: Creating a customer + + When I POST to /customers with the JSON API body: + """ + { + "name": "Customer name", + "email": "email@example.com" + } + """ + Then I should get a 201 Created status code + And the response is valid according to the "customers" schema + And the fields on this customer match: + """ + { + "name": "Customer name", + "email": "email@example.com" + } + """ diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 24e7577..417cb9b 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -4,8 +4,8 @@ "Accept" => $accept_header, }, basic_auth: { - username: @api_secret, - password: "", + username: @api_secret, + password: "", } } response = HTTParty.send(verb.downcase, "#{$root_url}#{url}", options) @@ -15,11 +15,12 @@ When(/^I POST to (\/.*) without my secret key with the JSON API body:$/) do |url, body| options = { - headers: { - "Accept" => $accept_header, - }, - body: JSON.parse(body) - } + headers: { + "Accept" => $accept_header, + }, + body: JSON.parse(body) + } + response = HTTParty.post("#{$root_url}#{url}", options) @response_code = response.code @response_body = JSON.parse(response.body) @@ -27,10 +28,11 @@ When(/^I POST to (\/.*) without my secret key$/) do |url| options = { - headers: { - "Accept" => $accept_header, - }, - } + headers: { + "Accept" => $accept_header, + }, + } + response = HTTParty.post("#{$root_url}#{url}", options) @response_code = response.code @response_body = JSON.parse(response.body) @@ -45,8 +47,8 @@ "Accept" => $accept_header, }, basic_auth: { - username: $api_secret, - password: "", + username: $api_secret, + password: "", } } @@ -55,6 +57,22 @@ @response_body = JSON.parse(response.body) end +When(/^I POST to (\/.*) with the JSON API body:$/) do |url, body| + options = { + headers: { + "Accept" => $accept_header, + }, + body: JSON.parse(body), + basic_auth: { + username: $api_secret, + password: "", + } + } + response = HTTParty.post("#{$root_url}#{url}", options) + @response_code = response.code + @response_body = JSON.parse(response.body) +end + require 'json-schema' Then(/^the response has this schema:$/) do |schema| begin @@ -80,3 +98,18 @@ Then(/^there should be no response body$/) do assert_nil @response_body end + +def checker(from, of, nesting) + assert_not_nil of, nesting + from.each_pair do |key, val| + if val.is_a? String or val.is_a? Integer + assert_equal val, of[key], "#{nesting}>#{key}" + else + checker val, of[key], "#{nesting}>#{key}" + end + end +end + +Then(/^the fields on this (.*) match:$/) do |resource, against| + checker JSON.parse(against), @response_body["#{resource}s"][0], '' +end diff --git a/fixtures/_models/customer.json b/fixtures/_models/customer.json index 8f063a1..5ad8461 100644 --- a/fixtures/_models/customer.json +++ b/fixtures/_models/customer.json @@ -4,69 +4,60 @@ "properties": { "id": { "type": "string", - "required": true, "pattern": "(CU|AC)[a-zA-Z0-9]{16,32}" }, "href": { "type": "string", - "format": "uri", - "required": true + "format": "uri" }, "created_at": { "type": "string", - "format": "date-time", - "required": true + "format": "date-time" }, "updated_at": { "type": "string", - "format": "date-time", - "required": true + "format": "date-time" }, "name": { "type": [ "null", "string" - ], - "required": true + ] }, "business_name": { "type": [ "null", "string" - ], - "required": true + ] }, "email": { "type": [ "null", "string" ], - "format": "email", - "required": true + "format": "email" + }, "phone": { "type": [ "null", "string" ], - "format": "phone", - "required": true + "format": "phone" }, "ssn_last4": { "type": [ "null", "string" ], - "pattern": "x{4}", - "required": true + "pattern": "x{4}" }, "ein": { "type": [ "null", "string" ], - "pattern": "[0-9]{9}", - "required": true + "pattern": "[0-9]{9}" }, "dob_month": { "type": [ @@ -74,32 +65,28 @@ "integer" ], "minimum": 1, - "maximum": 12, - "required": true + "maximum": 12 }, "dob_year": { "type": [ "null", "integer" - ], - "required": true + ] }, "merchant_status": { "type": "string", "enum": [ "need-more-information", "underwritten", - "rejected" - ], - "required": true + "rejected", + "no-match" + ] }, "address": { - "$ref": "address.json", - "required": true + "$ref": "address.json" }, "meta": { - "type": "object", - "required": true + "type": "object" }, "links": { "type": "object", @@ -109,21 +96,40 @@ "null", "string" ], - "pattern": "(CC|BA)[a-zA-Z0-9]{16,32}", - "required": true + "pattern": "(CC|BA)[a-zA-Z0-9]{16,32}" }, "destination": { "type": [ "null", "string" ], - "pattern": "BA[a-zA-Z0-9]{16,32}", - "required": true + "pattern": "BA[a-zA-Z0-9]{16,32}" } }, - "required": true, + "required": [ + "source", + "destination" + ], "additionalProperties": false } }, + "required": [ + "id", + "href", + "created_at", + "updated_at", + "name", + "business_name", + "email", + "phone", + "ssn_last4", + "ein", + "dob_month", + "dob_year", + "merchant_status", + "address", + "meta", + "links" + ], "additionalProperties": false -} \ No newline at end of file +} diff --git a/fixtures/customers.json b/fixtures/customers.json new file mode 100644 index 0000000..531d599 --- /dev/null +++ b/fixtures/customers.json @@ -0,0 +1,82 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "links": { + "type": "object", + "properties": { + "customers.cards": { + "type": "string", + "format": "uri", + "pattern": "/customers/{customers.id}/cards" + }, + "customers.bank_accounts": { + "type": "string", + "format": "uri", + "pattern": "/customers/{customers.id}/bank_accounts" + }, + "customers.credits": { + "type": "string", + "format": "uri", + "pattern": "/customers/{customers.id}/credits" + }, + "customers.debits": { + "type": "string", + "format": "uri", + "pattern": "/customers/{customers.id}/debits" + }, + "customers.refunds": { + "type": "string", + "format": "uri", + "pattern": "/customers/{customers.id}/refunds" + }, + "customers.reversals": { + "type": "string", + "format": "uri", + "pattern": "/customers/{customers.id}/reversals" + }, + "customers.orders": { + "description": "Orders in which the customer is either a buyer or merchant", + "type": "string", + "format": "uri", + "pattern": "/customers/{customers.id}/orders" + }, + "customers.source": { + "type": "string", + "format": "uri", + "pattern": "/resources/{customers.source}" + }, + "customers.destination": { + "type": "string", + "format": "uri", + "pattern": "/resources/{customers.destination}" + } + }, + "required": [ + "customers.cards", + "customers.bank_accounts", + "customers.credits", + "customers.debits", + "customers.refunds", + "customers.reversals", + "customers.orders", + "customers.source", + "customers.destination" + ] + }, + "meta": { + "type": "object" + }, + "customers": { + "items": { + "$ref": "_models/customer.json" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true + } + }, + "required": [ + "customers" + ] +} From 43349be6311ac2bf60be54e415896cba4f7af0d3 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Fri, 13 Dec 2013 18:34:30 -0800 Subject: [PATCH 015/154] micro library to contain interaction with the api --- features/customers.feature | 10 +++ features/step_definitions/api_keys.rb | 17 ++-- features/step_definitions/customers.rb | 7 ++ features/step_definitions/http_steps.rb | 79 +++++------------- features/support/initial_setup.rb | 7 ++ lib/min_json_api.rb | 101 ++++++++++++++++++++++++ 6 files changed, 152 insertions(+), 69 deletions(-) create mode 100644 features/step_definitions/customers.rb create mode 100644 lib/min_json_api.rb diff --git a/features/customers.feature b/features/customers.feature index eb88018..de5c76b 100644 --- a/features/customers.feature +++ b/features/customers.feature @@ -21,3 +21,13 @@ Feature: Customers "email": "email@example.com" } """ + + Scenario: testing stuff + When I GET to /customers + Then I should get a 200 OK status code + + # Scenario: List all customers + # Given I have created more than one Customer + # When I GET to /customers + # Then I should get a 200 OK status code + # And there should be more than two customers paged diff --git a/features/step_definitions/api_keys.rb b/features/step_definitions/api_keys.rb index 4609594..c46ddcb 100644 --- a/features/step_definitions/api_keys.rb +++ b/features/step_definitions/api_keys.rb @@ -5,10 +5,9 @@ }, } response = HTTParty.post("#{$root_url}/api_keys", options) - @response_code = response.code - @response_body = JSON.parse(response.body) - @api_secret = @response_body["api_keys"][0]["secret"] # ugh - @api_key = @response_body["api_keys"][0]["id"] + @client.RAW(response) + @api_secret = @client['secret'] + @api_key = @client['id'] end When(/^I GET to \/api_keys\/:api_key giving the key$/) do @@ -21,9 +20,8 @@ password: "", } } - response = HTTParty.get("#{$root_url}/api_keys/#{@api_key}", options) - @response_code = response.code - @response_body = JSON.parse(response.body) + response = HTTParty.get("#{@client.root_url}/api_keys/#{@api_key}", options) + @client.RAW(response) end When(/^I DELETE to \/api_keys\/:api_key giving the key$/) do @@ -36,9 +34,8 @@ password: "", } } - response = HTTParty.delete("#{$root_url}/api_keys/#{@api_key}", options) - @response_code = response.code - @response_body = response.body + response = HTTParty.delete("#{@client.root_url}/api_keys/#{@api_key}", options) + @client.RAW(response) end Given(/^I have created more than one API keys$/) do diff --git a/features/step_definitions/customers.rb b/features/step_definitions/customers.rb new file mode 100644 index 0000000..8b75add --- /dev/null +++ b/features/step_definitions/customers.rb @@ -0,0 +1,7 @@ +Given(/^I have created a Customer$/) do + +end + + +Given(/^I have created more than one Customer$/) do +end diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 417cb9b..60d1dd5 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -1,19 +1,9 @@ When(/^I (\w+) to (\/\S*?)$/) do |verb, url| - options = { - headers: { - "Accept" => $accept_header, - }, - basic_auth: { - username: @api_secret, - password: "", - } - } - response = HTTParty.send(verb.downcase, "#{$root_url}#{url}", options) - @response_code = response.code - @response_body = JSON.parse(response.body) + @client.verb(verb, url) end When(/^I POST to (\/.*) without my secret key with the JSON API body:$/) do |url, body| + # use for tokenizing cards and bank accounts options = { headers: { "Accept" => $accept_header, @@ -22,11 +12,11 @@ } response = HTTParty.post("#{$root_url}#{url}", options) - @response_code = response.code - @response_body = JSON.parse(response.body) + @client.RAW(response) end When(/^I POST to (\/.*) without my secret key$/) do |url| + # used for creating api keys for new marketplaces options = { headers: { "Accept" => $accept_header, @@ -34,73 +24,37 @@ } response = HTTParty.post("#{$root_url}#{url}", options) - @response_code = response.code - @response_body = JSON.parse(response.body) + @client.RAW(response) end When(/^I GET "(.*?)" from the previous response$/) do |keys| - # hax to access a Ruby hash like dot notation - # Array shennanigans is because we only support the first element - url = keys.split('.').inject(@response_body) {|o, k| Array(o[k])[0] } - options = { - headers: { - "Accept" => $accept_header, - }, - basic_auth: { - username: $api_secret, - password: "", - } - } - - response = HTTParty.get("#{$root_url}#{url}", options) - @response_code = response.code - @response_body = JSON.parse(response.body) + @client.GET(@client.inject keys) end When(/^I POST to (\/.*) with the JSON API body:$/) do |url, body| - options = { - headers: { - "Accept" => $accept_header, - }, - body: JSON.parse(body), - basic_auth: { - username: $api_secret, - password: "", - } - } - response = HTTParty.post("#{$root_url}#{url}", options) - @response_code = response.code - @response_body = JSON.parse(response.body) + @client.POST(url, body) end require 'json-schema' Then(/^the response has this schema:$/) do |schema| - begin - JSON::Validator.validate!(JSON.parse(schema), @response_body) - rescue JSON::Schema::ValidationError => e - flunk(e.message) - end + @client.validate(schema) end Then(/^the response is valid according to the "(.*?)" schema$/) do |filename| - file_name = File.join('fixtures', "#{filename}.json") - begin - JSON::Validator.validate!(file_name, @response_body) - rescue JSON::Schema::ValidationError => e - flunk(e.message) - end + @client.validate(filename) end Then(/^I should get a (.+) status code$/) do |code| - assert_equal code.to_i, @response_code + assert_equal code.to_i, @client.code end Then(/^there should be no response body$/) do - assert_nil @response_body + assert_nil @client.body end def checker(from, of, nesting) assert_not_nil of, nesting + from.each_pair do |key, val| if val.is_a? String or val.is_a? Integer assert_equal val, of[key], "#{nesting}>#{key}" @@ -111,5 +65,12 @@ def checker(from, of, nesting) end Then(/^the fields on this (.*) match:$/) do |resource, against| - checker JSON.parse(against), @response_body["#{resource}s"][0], '' + checker JSON.parse(against), @client.body["#{resource}s"][0], '' +end + +# TODO: move? + +Before do |scenario| + @client = Balanced::MinAPI::Client.new($api_secret, $accept_header, $root_url) + @client.running = scenario end diff --git a/features/support/initial_setup.rb b/features/support/initial_setup.rb index 637a88d..2c76b3a 100644 --- a/features/support/initial_setup.rb +++ b/features/support/initial_setup.rb @@ -1,6 +1,7 @@ require 'httparty' require 'json' +# TODO: move this into the lib $root_url = 'https://api.balancedpayments.com' $accept_header = 'application/vnd.api+json;revision=1.1' @@ -28,3 +29,9 @@ } HTTParty.post("#{$root_url}/marketplaces", options) + +$:.unshift(File.dirname(__FILE__)+'/../../lib') + +require 'min_json_api' + +$client = Balanced::MinAPI::Client.new($api_secret, $accept_header, $root_url) diff --git a/lib/min_json_api.rb b/lib/min_json_api.rb new file mode 100644 index 0000000..6b11172 --- /dev/null +++ b/lib/min_json_api.rb @@ -0,0 +1,101 @@ +require 'json-schema' + +module Balanced + module MinAPI + class Client + attr_reader :api_secret, :root_url + attr_reader :responses + attr_writer :running + + def initialize(api_secret, accept_header, root_url) + @api_secret = api_secret + @accept_header = accept_header + @root_url = root_url + + @responses = [] + end + + def POST (endpoint, body) + options = { + headers: { + 'Accept' => @accept_header, + }, + body: JSON.parse(body), + basic_auth: { + username: @api_secret, + password: '', + } + } + + response = HTTParty.post("#{@root_url}#{endpoint}", options) + @responses << response + response + end + + def GET(endpoint) + verb 'GET', endpoint + end + + def RAW(response) + @responses << response + end + + def verb (verb, url) + options = { + headers: { + 'Accept' => @accept_header + }, + basic_auth: { + username: @api_secret, + password: '', + } + } + response = HTTParty.send(verb.downcase, "#{$root_url}#{url}", options) + @responses << response + response + end + + def code + @responses.last.code + end + + def body + if @responses.last.body + JSON.parse(@responses.last.body) + end + end + + def inject (key) + # hax to access a Ruby hash like dot notation + key.split('.').inject(body) {|o, k| Array(o[k])[0] } + end + + def validate(against) + file_name = File.join('fixtures', "#{against}.json") + if File.exists? file_name and not against.is_a? Hash + begin + JSON::Validator.validate!(file_name, body) + rescue JSON::Schema::ValidationError + assert false, "The response failed the '#{filename}' schema. With error: #{$!.message}\nHere's the body: #{@response.last.body}\n" + end + else + begin + JSON::Validator.validate!(against, body) + rescue JSON::Schema::ValidationError + assert false, $!.message + end + end + end + + def [](name) + # either access some type by name, or access a field on an object if there was only one + if body.has_key? name + body[name][0] + else + body.select { |x| x != 'links' and x != 'meta' }.values.first.first[name] + end + end + + end + end +end From c318fb3f81f02c5494eeae0813321b29b221d6c6 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Fri, 13 Dec 2013 19:06:48 -0800 Subject: [PATCH 016/154] New feature: Customer index --- features/customers.feature | 10 +++------- features/step_definitions/customers.rb | 3 ++- features/step_definitions/http_steps.rb | 6 +++++- lib/min_json_api.rb | 3 ++- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/features/customers.feature b/features/customers.feature index de5c76b..d0d31c1 100644 --- a/features/customers.feature +++ b/features/customers.feature @@ -22,12 +22,8 @@ Feature: Customers } """ - Scenario: testing stuff + Scenario: List all customers + Given I have created more than one Customer When I GET to /customers Then I should get a 200 OK status code - - # Scenario: List all customers - # Given I have created more than one Customer - # When I GET to /customers - # Then I should get a 200 OK status code - # And there should be more than two customers paged + And there should be more than two customers paged diff --git a/features/step_definitions/customers.rb b/features/step_definitions/customers.rb index 8b75add..05169d2 100644 --- a/features/step_definitions/customers.rb +++ b/features/step_definitions/customers.rb @@ -1,7 +1,8 @@ Given(/^I have created a Customer$/) do - + @client.POST('/customers', {}) end Given(/^I have created more than one Customer$/) do + 2.times { step "I have created a Customer" } end diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 60d1dd5..a3875df 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -65,7 +65,11 @@ def checker(from, of, nesting) end Then(/^the fields on this (.*) match:$/) do |resource, against| - checker JSON.parse(against), @client.body["#{resource}s"][0], '' + checker JSON.parse(against), @client["#{resource}s"], '' +end + +Then(/^there should be more than two (.*) paged$/) do |name| + assert @client.body[name].size >= 2, "There were not more than two #{name}" end # TODO: move? diff --git a/lib/min_json_api.rb b/lib/min_json_api.rb index 6b11172..1b74240 100644 --- a/lib/min_json_api.rb +++ b/lib/min_json_api.rb @@ -16,11 +16,12 @@ def initialize(api_secret, accept_header, root_url) end def POST (endpoint, body) + body = JSON.parse(body) if body.is_a? String options = { headers: { 'Accept' => @accept_header, }, - body: JSON.parse(body), + body: body, basic_auth: { username: @api_secret, password: '', From 26bbee3d1243cfc8e185d76e6b137095394d6b5a Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 16 Dec 2013 22:20:48 +0000 Subject: [PATCH 017/154] clean up some whitespace --- features/customers.feature | 1 - 1 file changed, 1 deletion(-) diff --git a/features/customers.feature b/features/customers.feature index d0d31c1..6a5f5f8 100644 --- a/features/customers.feature +++ b/features/customers.feature @@ -4,7 +4,6 @@ Feature: Customers All debit and credits can be tracked for a specific customer. Scenario: Creating a customer - When I POST to /customers with the JSON API body: """ { From f385d4f5125421a955e2256c2bb6da7b460c8992 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 16 Dec 2013 22:28:28 +0000 Subject: [PATCH 018/154] New feature: get customer --- features/customers.feature | 6 ++++++ features/step_definitions/customers.rb | 16 +++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/features/customers.feature b/features/customers.feature index 6a5f5f8..41425c0 100644 --- a/features/customers.feature +++ b/features/customers.feature @@ -21,6 +21,12 @@ Feature: Customers } """ + Scenario: Get a customer + Given I have created a Customer + When I GET to /customers/:customer_id giving the customer_id + Then I should get a 200 OK status code + And the response is valid according to the "customers" schema + Scenario: List all customers Given I have created more than one Customer When I GET to /customers diff --git a/features/step_definitions/customers.rb b/features/step_definitions/customers.rb index 05169d2..d3c419c 100644 --- a/features/step_definitions/customers.rb +++ b/features/step_definitions/customers.rb @@ -1,8 +1,22 @@ Given(/^I have created a Customer$/) do @client.POST('/customers', {}) + @customer_id = @client['id'] end - Given(/^I have created more than one Customer$/) do 2.times { step "I have created a Customer" } end + +When(/^I GET to \/customers\/:customer_id giving the customer_id$/) do + options = { + headers: { + "Accept" => $accept_header, + }, + basic_auth: { + username: $api_secret, + password: "", + } + } + response = HTTParty.get("#{@client.root_url}/customers/#{@customer_id}", options) + @client.RAW(response) +end From aeb357e46ddaa04634ec341aedcc7819a0653871 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 16 Dec 2013 22:33:29 +0000 Subject: [PATCH 019/154] 'fix' failing address spec. This was failing, but for the wrong reasons. Let's fail for the right ones. --- features/step_definitions/http_steps.rb | 6 +++--- lib/min_json_api.rb | 12 ++---------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index a3875df..0e08e6c 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -2,7 +2,7 @@ @client.verb(verb, url) end -When(/^I POST to (\/.*) without my secret key with the JSON API body:$/) do |url, body| +When(/^I POST to (\/\S*) without my secret key with the JSON API body:$/) do |url, body| # use for tokenizing cards and bank accounts options = { headers: { @@ -15,7 +15,7 @@ @client.RAW(response) end -When(/^I POST to (\/.*) without my secret key$/) do |url| +When(/^I POST to (\/\S*) without my secret key$/) do |url| # used for creating api keys for new marketplaces options = { headers: { @@ -31,7 +31,7 @@ @client.GET(@client.inject keys) end -When(/^I POST to (\/.*) with the JSON API body:$/) do |url, body| +When(/^I POST to (\/\S*) with the JSON API body:$/) do |url, body| @client.POST(url, body) end diff --git a/lib/min_json_api.rb b/lib/min_json_api.rb index 1b74240..7e91bad 100644 --- a/lib/min_json_api.rb +++ b/lib/min_json_api.rb @@ -74,17 +74,9 @@ def inject (key) def validate(against) file_name = File.join('fixtures', "#{against}.json") if File.exists? file_name and not against.is_a? Hash - begin - JSON::Validator.validate!(file_name, body) - rescue JSON::Schema::ValidationError - assert false, "The response failed the '#{filename}' schema. With error: #{$!.message}\nHere's the body: #{@response.last.body}\n" - end + JSON::Validator.validate!(file_name, body) else - begin - JSON::Validator.validate!(against, body) - rescue JSON::Schema::ValidationError - assert false, $!.message - end + JSON::Validator.validate!(against, body) end end From c66f6659e01094c5c891700863145d34e19ae270 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 16 Dec 2013 22:42:38 +0000 Subject: [PATCH 020/154] Clean up some small formatting issues. I missed these in my earlier cleanups. --- lib/min_json_api.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/min_json_api.rb b/lib/min_json_api.rb index 7e91bad..010a92d 100644 --- a/lib/min_json_api.rb +++ b/lib/min_json_api.rb @@ -66,7 +66,7 @@ def body end end - def inject (key) + def inject(key) # hax to access a Ruby hash like dot notation key.split('.').inject(body) {|o, k| Array(o[k])[0] } end @@ -88,7 +88,6 @@ def [](name) body.select { |x| x != 'links' and x != 'meta' }.values.first.first[name] end end - end end end From 4d053643562983320ec4605c99dd8dc82fc1d4f2 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 16 Dec 2013 23:00:07 +0000 Subject: [PATCH 021/154] New feature: remove a customer --- features/customers.feature | 6 ++++++ features/step_definitions/customers.rb | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/features/customers.feature b/features/customers.feature index 41425c0..d3652ea 100644 --- a/features/customers.feature +++ b/features/customers.feature @@ -32,3 +32,9 @@ Feature: Customers When I GET to /customers Then I should get a 200 OK status code And there should be more than two customers paged + + Scenario: Remove a customer + Given I have created a Customer + When I DELETE to /customers/:customer_id giving the customer_id + Then I should get a 204 OK status code + And there should be no response body diff --git a/features/step_definitions/customers.rb b/features/step_definitions/customers.rb index d3c419c..26000b8 100644 --- a/features/step_definitions/customers.rb +++ b/features/step_definitions/customers.rb @@ -20,3 +20,17 @@ response = HTTParty.get("#{@client.root_url}/customers/#{@customer_id}", options) @client.RAW(response) end + +When(/^I DELETE to \/customers\/:customer_id giving the customer_id$/) do + options = { + headers: { + "Accept" => $accept_header, + }, + basic_auth: { + username: $api_secret, + password: "", + } + } + response = HTTParty.delete("#{@client.root_url}/customers/#{@customer_id}", options) + @client.RAW(response) +end From 146c870951351e8b5e6878ed306f1683a2f0e56c Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 16 Dec 2013 23:11:03 +0000 Subject: [PATCH 022/154] New feature: create a callback --- features/callbacks.feature | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 features/callbacks.feature diff --git a/features/callbacks.feature b/features/callbacks.feature new file mode 100644 index 0000000..13cd025 --- /dev/null +++ b/features/callbacks.feature @@ -0,0 +1,11 @@ +Feature: Callbacks + + Scenario: Create a callback + When I POST to /callbacks with the JSON API body: + """ + { + "url":"http://www.example.com/callback" + } + """ + Then I should get a 201 Created status code + # TODO: Callback schema? From 6b9aae416588717bdefdad8df1dc5f11b50d9bb1 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 16 Dec 2013 23:24:53 +0000 Subject: [PATCH 023/154] New feature: get callback info --- features/callbacks.feature | 8 +++++- features/step_definitions/callbacks.rb | 36 ++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 features/step_definitions/callbacks.rb diff --git a/features/callbacks.feature b/features/callbacks.feature index 13cd025..aad5466 100644 --- a/features/callbacks.feature +++ b/features/callbacks.feature @@ -8,4 +8,10 @@ Feature: Callbacks } """ Then I should get a 201 Created status code - # TODO: Callback schema? + # TODO: Callback schema? + + Scenario: Retrieve information about an existing callback + Given I have created a callback + When I GET to /callbacks/:callback_id giving the callback_id + Then I should get a 200 OK status code + # TODO: Callback schema? diff --git a/features/step_definitions/callbacks.rb b/features/step_definitions/callbacks.rb new file mode 100644 index 0000000..8313949 --- /dev/null +++ b/features/step_definitions/callbacks.rb @@ -0,0 +1,36 @@ +Given(/^I have created a callback$/) do + @client.POST('/callbacks', {}) + @callback_id = @client['id'] +end + +Given(/^I have created more than one callback$/) do + 2.times { step "I have created a callback" } +end + +When(/^I GET to \/callbacks\/:callback_id giving the callback_id$/) do + options = { + headers: { + "Accept" => $accept_header, + }, + basic_auth: { + username: $api_secret, + password: "", + } + } + response = HTTParty.get("#{@client.root_url}/callbacks/#{@callback_id}", options) + @client.RAW(response) +end + +When(/^I DELETE to \/callbacks\/:callback_id giving the callback_id$/) do + options = { + headers: { + "Accept" => $accept_header, + }, + basic_auth: { + username: $api_secret, + password: "", + } + } + response = HTTParty.delete("#{@client.root_url}/callbacks/#{@callback_id}", options) + @client.RAW(response) +end From 767de1ec52d1cbab02a20f015672717740c06015 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 17 Dec 2013 01:21:05 +0000 Subject: [PATCH 024/154] refactor to use the client Thanks @matthewfl --- features/step_definitions/customers.rb | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/features/step_definitions/customers.rb b/features/step_definitions/customers.rb index 26000b8..75bdcdc 100644 --- a/features/step_definitions/customers.rb +++ b/features/step_definitions/customers.rb @@ -8,17 +8,7 @@ end When(/^I GET to \/customers\/:customer_id giving the customer_id$/) do - options = { - headers: { - "Accept" => $accept_header, - }, - basic_auth: { - username: $api_secret, - password: "", - } - } - response = HTTParty.get("#{@client.root_url}/customers/#{@customer_id}", options) - @client.RAW(response) + @client.GET("/customers/#{@customer_id}") end When(/^I DELETE to \/customers\/:customer_id giving the customer_id$/) do From c88e3e73b650e319273ac027897b4a4ba52f2b1f Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 17 Dec 2013 17:46:41 +0000 Subject: [PATCH 025/154] New features: list/delete callbacks --- features/callbacks.feature | 13 +++++++++++++ features/step_definitions/callbacks.rb | 12 +----------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/features/callbacks.feature b/features/callbacks.feature index aad5466..4f984d2 100644 --- a/features/callbacks.feature +++ b/features/callbacks.feature @@ -15,3 +15,16 @@ Feature: Callbacks When I GET to /callbacks/:callback_id giving the callback_id Then I should get a 200 OK status code # TODO: Callback schema? + + Scenario: List all callbacks + Given I have created more than one callback + When I GET to /callbacks + Then I should get a 200 OK status code + # TODO: Callback schema? + + @failing + Scenario: Remove a callback + Given I have created a callback + When I DELETE to /callbacks/:callback_id giving the callback_id + Then I should get a 204 OK status code + And there should be no response body diff --git a/features/step_definitions/callbacks.rb b/features/step_definitions/callbacks.rb index 8313949..f331281 100644 --- a/features/step_definitions/callbacks.rb +++ b/features/step_definitions/callbacks.rb @@ -8,17 +8,7 @@ end When(/^I GET to \/callbacks\/:callback_id giving the callback_id$/) do - options = { - headers: { - "Accept" => $accept_header, - }, - basic_auth: { - username: $api_secret, - password: "", - } - } - response = HTTParty.get("#{@client.root_url}/callbacks/#{@callback_id}", options) - @client.RAW(response) + @client.GET("/callbacks/#{@callback_id}") end When(/^I DELETE to \/callbacks\/:callback_id giving the callback_id$/) do From 9a4cb93b6a2b8ed6bc884262c7b0f3ce66580862 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 17 Dec 2013 18:00:27 +0000 Subject: [PATCH 026/154] New feature: get a card --- features/cards.feature | 7 +++++++ features/step_definitions/cards.rb | 32 ++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 features/step_definitions/cards.rb diff --git a/features/cards.feature b/features/cards.feature index 56d55e3..07f3c11 100644 --- a/features/cards.feature +++ b/features/cards.feature @@ -27,3 +27,10 @@ Feature: Tokenize a credit card When I GET "cards.href" from the previous response Then I should get a 200 OK status code And the response is valid according to the "cards" schema + + @failing + Scenario: Retrieve a card + Given I have tokenized a card + When I GET to /cards/:card_id giving the card_id + Then I should get a 200 OK status code + And the response is valid according to the "cards" schema diff --git a/features/step_definitions/cards.rb b/features/step_definitions/cards.rb new file mode 100644 index 0000000..7c1c3a0 --- /dev/null +++ b/features/step_definitions/cards.rb @@ -0,0 +1,32 @@ +Given(/^I have tokenized a card$/) do + @client.POST('/cards', + { + number: "4111111111111111", + expiration_month: "12", + expiration_year: 2016 + } + ) + @card_id = @client['cards']['id'] +end + +Given(/^I have created more than one card$/) do + 2.times { step "I have created a card" } +end + +When(/^I GET to \/cards\/:card_id giving the card_id$/) do + @client.GET("/cards/#{@card_id}") +end + +When(/^I DELETE to \/cards\/:card_id giving the card_id$/) do + options = { + headers: { + "Accept" => $accept_header, + }, + basic_auth: { + username: $api_secret, + password: "", + } + } + response = HTTParty.delete("#{@client.root_url}/cards/#{@card_id}", options) + @client.RAW(response) +end From 9a6603e8abfb7d4e8b6f78380ee9a1353b6ddba4 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 17 Dec 2013 18:05:14 +0000 Subject: [PATCH 027/154] New features: list and remove cards --- features/cards.feature | 13 +++++++++++++ features/step_definitions/cards.rb | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/features/cards.feature b/features/cards.feature index 07f3c11..81cb937 100644 --- a/features/cards.feature +++ b/features/cards.feature @@ -34,3 +34,16 @@ Feature: Tokenize a credit card When I GET to /cards/:card_id giving the card_id Then I should get a 200 OK status code And the response is valid according to the "cards" schema + + @failing + Scenario: List all cards + Given I have tokenized more than one card + When I GET to /cards + Then I should get a 200 OK status code + And the response is valid according to the "cards" schema + + Scenario: Remove a card + Given I have tokenized a card + When I DELETE to /cards/:card_id giving the card_id + Then I should get a 204 OK status code + And there should be no response body diff --git a/features/step_definitions/cards.rb b/features/step_definitions/cards.rb index 7c1c3a0..667c538 100644 --- a/features/step_definitions/cards.rb +++ b/features/step_definitions/cards.rb @@ -9,8 +9,8 @@ @card_id = @client['cards']['id'] end -Given(/^I have created more than one card$/) do - 2.times { step "I have created a card" } +Given(/^I have tokenized more than one card$/) do + 2.times { step "I have tokenized a card" } end When(/^I GET to \/cards\/:card_id giving the card_id$/) do From 35776fa3524948af9b9dfcc922b0fdb667051ff7 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 17 Dec 2013 18:22:22 +0000 Subject: [PATCH 028/154] client library refactor. I took @matthewfl's work on the client library and fixed it up a bit. There were some idiom issues, some conventions that we're followed, and a couple of OOP things that _really_ bugged me. I'm happy with this now, other than two things: we are doing OO terribly with this whole response thing, and we should also implement DELETE. --- features/step_definitions/api_keys.rb | 6 +- features/step_definitions/callbacks.rb | 6 +- features/step_definitions/cards.rb | 6 +- features/step_definitions/customers.rb | 6 +- features/step_definitions/http_steps.rb | 16 ++--- features/support/initial_setup.rb | 4 +- lib/balanced/tiny_client.rb | 91 ++++++++++++++++++++++++ lib/min_json_api.rb | 93 ------------------------- 8 files changed, 113 insertions(+), 115 deletions(-) create mode 100644 lib/balanced/tiny_client.rb delete mode 100644 lib/min_json_api.rb diff --git a/features/step_definitions/api_keys.rb b/features/step_definitions/api_keys.rb index c46ddcb..23af468 100644 --- a/features/step_definitions/api_keys.rb +++ b/features/step_definitions/api_keys.rb @@ -5,7 +5,7 @@ }, } response = HTTParty.post("#{$root_url}/api_keys", options) - @client.RAW(response) + @client.add_response(response) @api_secret = @client['secret'] @api_key = @client['id'] end @@ -21,7 +21,7 @@ } } response = HTTParty.get("#{@client.root_url}/api_keys/#{@api_key}", options) - @client.RAW(response) + @client.add_response(response) end When(/^I DELETE to \/api_keys\/:api_key giving the key$/) do @@ -35,7 +35,7 @@ } } response = HTTParty.delete("#{@client.root_url}/api_keys/#{@api_key}", options) - @client.RAW(response) + @client.add_response(response) end Given(/^I have created more than one API keys$/) do diff --git a/features/step_definitions/callbacks.rb b/features/step_definitions/callbacks.rb index f331281..2c68911 100644 --- a/features/step_definitions/callbacks.rb +++ b/features/step_definitions/callbacks.rb @@ -1,5 +1,5 @@ Given(/^I have created a callback$/) do - @client.POST('/callbacks', {}) + @client.post('/callbacks', {}) @callback_id = @client['id'] end @@ -8,7 +8,7 @@ end When(/^I GET to \/callbacks\/:callback_id giving the callback_id$/) do - @client.GET("/callbacks/#{@callback_id}") + @client.get("/callbacks/#{@callback_id}") end When(/^I DELETE to \/callbacks\/:callback_id giving the callback_id$/) do @@ -22,5 +22,5 @@ } } response = HTTParty.delete("#{@client.root_url}/callbacks/#{@callback_id}", options) - @client.RAW(response) + @client.add_response(response) end diff --git a/features/step_definitions/cards.rb b/features/step_definitions/cards.rb index 667c538..4cf0008 100644 --- a/features/step_definitions/cards.rb +++ b/features/step_definitions/cards.rb @@ -1,5 +1,5 @@ Given(/^I have tokenized a card$/) do - @client.POST('/cards', + @client.post('/cards', { number: "4111111111111111", expiration_month: "12", @@ -14,7 +14,7 @@ end When(/^I GET to \/cards\/:card_id giving the card_id$/) do - @client.GET("/cards/#{@card_id}") + @client.get("/cards/#{@card_id}") end When(/^I DELETE to \/cards\/:card_id giving the card_id$/) do @@ -28,5 +28,5 @@ } } response = HTTParty.delete("#{@client.root_url}/cards/#{@card_id}", options) - @client.RAW(response) + @client.add_response(response) end diff --git a/features/step_definitions/customers.rb b/features/step_definitions/customers.rb index 75bdcdc..9c26a9d 100644 --- a/features/step_definitions/customers.rb +++ b/features/step_definitions/customers.rb @@ -1,5 +1,5 @@ Given(/^I have created a Customer$/) do - @client.POST('/customers', {}) + @client.post('/customers', {}) @customer_id = @client['id'] end @@ -8,7 +8,7 @@ end When(/^I GET to \/customers\/:customer_id giving the customer_id$/) do - @client.GET("/customers/#{@customer_id}") + @client.get("/customers/#{@customer_id}") end When(/^I DELETE to \/customers\/:customer_id giving the customer_id$/) do @@ -22,5 +22,5 @@ } } response = HTTParty.delete("#{@client.root_url}/customers/#{@customer_id}", options) - @client.RAW(response) + @client.add_response(response) end diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 0e08e6c..0ba5d23 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -12,7 +12,7 @@ } response = HTTParty.post("#{$root_url}#{url}", options) - @client.RAW(response) + @client.add_response(response) end When(/^I POST to (\/\S*) without my secret key$/) do |url| @@ -24,15 +24,15 @@ } response = HTTParty.post("#{$root_url}#{url}", options) - @client.RAW(response) + @client.add_response(response) end When(/^I GET "(.*?)" from the previous response$/) do |keys| - @client.GET(@client.inject keys) + @client.get(@client.inject keys) end When(/^I POST to (\/\S*) with the JSON API body:$/) do |url, body| - @client.POST(url, body) + @client.post(url, body) end require 'json-schema' @@ -45,11 +45,11 @@ end Then(/^I should get a (.+) status code$/) do |code| - assert_equal code.to_i, @client.code + assert_equal code.to_i, @client.last_code end Then(/^there should be no response body$/) do - assert_nil @client.body + assert_nil @client.last_body end def checker(from, of, nesting) @@ -69,12 +69,12 @@ def checker(from, of, nesting) end Then(/^there should be more than two (.*) paged$/) do |name| - assert @client.body[name].size >= 2, "There were not more than two #{name}" + assert @client.last_body[name].size >= 2, "There were not more than two #{name}" end # TODO: move? Before do |scenario| - @client = Balanced::MinAPI::Client.new($api_secret, $accept_header, $root_url) + @client = Balanced::TinyClient.new($api_secret, $accept_header, $root_url) @client.running = scenario end diff --git a/features/support/initial_setup.rb b/features/support/initial_setup.rb index 2c76b3a..fae20c7 100644 --- a/features/support/initial_setup.rb +++ b/features/support/initial_setup.rb @@ -32,6 +32,6 @@ $:.unshift(File.dirname(__FILE__)+'/../../lib') -require 'min_json_api' +require 'balanced/tiny_client' -$client = Balanced::MinAPI::Client.new($api_secret, $accept_header, $root_url) +$client = Balanced::TinyClient.new($api_secret, $accept_header, $root_url) diff --git a/lib/balanced/tiny_client.rb b/lib/balanced/tiny_client.rb new file mode 100644 index 0000000..d3fef82 --- /dev/null +++ b/lib/balanced/tiny_client.rb @@ -0,0 +1,91 @@ +require 'json-schema' + +module Balanced + class TinyClient + attr_reader :api_secret, :root_url + attr_reader :responses + attr_writer :running + + def initialize(api_secret, accept_header, root_url) + @api_secret = api_secret + @accept_header = accept_header + @root_url = root_url + + @responses = [] + end + + def post(endpoint, body) + body = JSON.parse(body) if body.is_a? String + options = { + headers: { + 'Accept' => @accept_header, + }, + body: body, + basic_auth: { + username: @api_secret, + password: '', + } + } + + response = HTTParty.post("#{@root_url}#{endpoint}", options) + @responses << response + response + end + + def get(endpoint) + verb 'GET', endpoint + end + + def add_response(response) + @responses << response + end + + def verb(verb, url) + options = { + headers: { + 'Accept' => @accept_header + }, + basic_auth: { + username: @api_secret, + password: '', + } + } + response = HTTParty.send(verb.downcase, "#{$root_url}#{url}", options) + @responses << response + response + end + + def last_code + @responses.last.code + end + + def last_body + if @responses.last.body + JSON.parse(@responses.last.body) + end + end + + def inject(key) + # hax to access a Ruby hash like dot notation + key.split('.').inject(last_body) {|o, k| Array(o[k])[0] } + end + + def validate(against) + file_name = File.join('fixtures', "#{against}.json") + if File.exists? file_name and not against.is_a? Hash + JSON::Validator.validate!(file_name, last_body) + else + JSON::Validator.validate!(against, last_body) + end + end + + def [](name) + # either access some type by name, or access a field on an object if there was only one + if last_body.has_key? name + last_body[name][0] + else + last_body.select { |x| x != 'links' and x != 'meta' }.values.first.first[name] + end + end + end +end diff --git a/lib/min_json_api.rb b/lib/min_json_api.rb deleted file mode 100644 index 010a92d..0000000 --- a/lib/min_json_api.rb +++ /dev/null @@ -1,93 +0,0 @@ -require 'json-schema' - -module Balanced - module MinAPI - class Client - attr_reader :api_secret, :root_url - attr_reader :responses - attr_writer :running - - def initialize(api_secret, accept_header, root_url) - @api_secret = api_secret - @accept_header = accept_header - @root_url = root_url - - @responses = [] - end - - def POST (endpoint, body) - body = JSON.parse(body) if body.is_a? String - options = { - headers: { - 'Accept' => @accept_header, - }, - body: body, - basic_auth: { - username: @api_secret, - password: '', - } - } - - response = HTTParty.post("#{@root_url}#{endpoint}", options) - @responses << response - response - end - - def GET(endpoint) - verb 'GET', endpoint - end - - def RAW(response) - @responses << response - end - - def verb (verb, url) - options = { - headers: { - 'Accept' => @accept_header - }, - basic_auth: { - username: @api_secret, - password: '', - } - } - response = HTTParty.send(verb.downcase, "#{$root_url}#{url}", options) - @responses << response - response - end - - def code - @responses.last.code - end - - def body - if @responses.last.body - JSON.parse(@responses.last.body) - end - end - - def inject(key) - # hax to access a Ruby hash like dot notation - key.split('.').inject(body) {|o, k| Array(o[k])[0] } - end - - def validate(against) - file_name = File.join('fixtures', "#{against}.json") - if File.exists? file_name and not against.is_a? Hash - JSON::Validator.validate!(file_name, body) - else - JSON::Validator.validate!(against, body) - end - end - - def [](name) - # either access some type by name, or access a field on an object if there was only one - if body.has_key? name - body[name][0] - else - body.select { |x| x != 'links' and x != 'meta' }.values.first.first[name] - end - end - end - end -end From 1e61843053229cddb6ee6962b7ea4c9018b30355 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Tue, 17 Dec 2013 10:55:28 -0800 Subject: [PATCH 029/154] adding hydrater --- features/debits.feature | 14 ++++++++++++++ features/step_definitions/cards.rb | 3 ++- features/step_definitions/debits.rb | 0 features/step_definitions/http_steps.rb | 2 +- lib/balanced/tiny_client.rb | 15 +++++++++++++++ 5 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 features/debits.feature create mode 100644 features/step_definitions/debits.rb diff --git a/features/debits.feature b/features/debits.feature new file mode 100644 index 0000000..52ef6c2 --- /dev/null +++ b/features/debits.feature @@ -0,0 +1,14 @@ +Feature: Debit a card or bank account + Debits is the action of charging a customers card or bank account. + Upon a debit succeeding, the value will be reflected in the marketplace's escrow balance + or in the escrow for an order. + + Scenario: Debiting a card + Given I have tokenized a card + When I POST to /cards/:card_id/debits with the JSON API body: + """ + { + "amount": 20000 + } + """ + Then I should get a 201 Created status code diff --git a/features/step_definitions/cards.rb b/features/step_definitions/cards.rb index 4cf0008..88217f1 100644 --- a/features/step_definitions/cards.rb +++ b/features/step_definitions/cards.rb @@ -1,5 +1,5 @@ Given(/^I have tokenized a card$/) do - @client.post('/cards', + @client.post('/cards', { number: "4111111111111111", expiration_month: "12", @@ -7,6 +7,7 @@ } ) @card_id = @client['cards']['id'] + @client.add_hydrate(:card_id, @card_id) end Given(/^I have tokenized more than one card$/) do diff --git a/features/step_definitions/debits.rb b/features/step_definitions/debits.rb new file mode 100644 index 0000000..e69de29 diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 0ba5d23..7a531bb 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -32,7 +32,7 @@ end When(/^I POST to (\/\S*) with the JSON API body:$/) do |url, body| - @client.post(url, body) + @client.post(@client.hydrater(url), body) end require 'json-schema' diff --git a/lib/balanced/tiny_client.rb b/lib/balanced/tiny_client.rb index d3fef82..beaac16 100644 --- a/lib/balanced/tiny_client.rb +++ b/lib/balanced/tiny_client.rb @@ -12,6 +12,7 @@ def initialize(api_secret, accept_header, root_url) @root_url = root_url @responses = [] + @hydrate_tokens = {} end def post(endpoint, body) @@ -70,6 +71,20 @@ def inject(key) key.split('.').inject(last_body) {|o, k| Array(o[k])[0] } end + def hydrater(what) + @hydrate_tokens.each_pair do |key, value| + if key.class == Symbol + key = ":#{key}" + end + what = what.gsub(key, value) + end + what + end + + def add_hydrate(key, value) + @hydrate_tokens[key] = value + end + def validate(against) file_name = File.join('fixtures', "#{against}.json") if File.exists? file_name and not against.is_a? Hash From bbb2effb4d656a9ca49cea088b4dfda4bf12838b Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Tue, 17 Dec 2013 11:03:07 -0800 Subject: [PATCH 030/154] start changing over to hydrater --- features/cards.feature | 2 +- features/step_definitions/cards.rb | 27 +++++++++++++------------ features/step_definitions/http_steps.rb | 2 +- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/features/cards.feature b/features/cards.feature index 81cb937..db8e6d8 100644 --- a/features/cards.feature +++ b/features/cards.feature @@ -44,6 +44,6 @@ Feature: Tokenize a credit card Scenario: Remove a card Given I have tokenized a card - When I DELETE to /cards/:card_id giving the card_id + When I DELETE to /cards/:card_id Then I should get a 204 OK status code And there should be no response body diff --git a/features/step_definitions/cards.rb b/features/step_definitions/cards.rb index 88217f1..2bda115 100644 --- a/features/step_definitions/cards.rb +++ b/features/step_definitions/cards.rb @@ -18,16 +18,17 @@ @client.get("/cards/#{@card_id}") end -When(/^I DELETE to \/cards\/:card_id giving the card_id$/) do - options = { - headers: { - "Accept" => $accept_header, - }, - basic_auth: { - username: $api_secret, - password: "", - } - } - response = HTTParty.delete("#{@client.root_url}/cards/#{@card_id}", options) - @client.add_response(response) -end +# When(/^I DELETE to \/cards\/:card_id giving the card_id$/) do +# @client.delete( +# # options = { +# # headers: { +# # "Accept" => $accept_header, +# # }, +# # basic_auth: { +# # username: $api_secret, +# # password: "", +# # } +# # } +# # response = HTTParty.delete("#{@client.root_url}/cards/#{@card_id}", options) +# # @client.add_response(response) +# end diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 7a531bb..0364311 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -1,5 +1,5 @@ When(/^I (\w+) to (\/\S*?)$/) do |verb, url| - @client.verb(verb, url) + @client.verb(verb, @client.hydrater(url)) end When(/^I POST to (\/\S*) without my secret key with the JSON API body:$/) do |url, body| From 7caf6fd291211664f82aa6e3456426af8c0b4d20 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Tue, 17 Dec 2013 11:24:43 -0800 Subject: [PATCH 031/154] feature: debiting a card --- features/debits.feature | 7 ++++ fixtures/_models/debit.json | 72 ++++++++++++++++++++----------------- fixtures/debits.json | 57 +++++++++++++++++++++++++++++ lib/balanced/tiny_client.rb | 18 +++++++--- 4 files changed, 118 insertions(+), 36 deletions(-) create mode 100644 fixtures/debits.json diff --git a/features/debits.feature b/features/debits.feature index 52ef6c2..b7a60ed 100644 --- a/features/debits.feature +++ b/features/debits.feature @@ -12,3 +12,10 @@ Feature: Debit a card or bank account } """ Then I should get a 201 Created status code + And the response is valid according to the "debits" schema + And the fields on this debit match: + """ + { + "amount": 20000 + } + """ diff --git a/fixtures/_models/debit.json b/fixtures/_models/debit.json index f414f4b..561500f 100644 --- a/fixtures/_models/debit.json +++ b/fixtures/_models/debit.json @@ -4,98 +4,106 @@ "properties": { "id": { "type": "string", - "required": true, "pattern": "WD[a-zA-Z0-9]{16,32}" }, "href": { "type": "string", - "format": "uri", - "required": true + "format": "uri" }, "created_at": { "type": "string", - "format": "date-time", - "required": true + "format": "date-time" }, "updated_at": { "type": "string", - "format": "date-time", - "required": true + "format": "date-time" }, "amount": { "type": "integer", - "minimum": 1, - "required": true + "minimum": 1 }, "currency": { "type": "string", "enum": [ "USD" - ], - "required": true + ] }, "appears_on_statement_as": { "type": "string", - "pattern": "BAL\\*[a-zA-Z0-9 \\.\\-]{1,18}", - "required": true + "pattern": "BAL\\*[a-zA-Z0-9 \\.\\-]{1,18}" }, "description": { "type": [ "string", "null" - ], - "required": true + ] }, "status": { - "$ref": "status.json", - "required": true + "$ref": "status.json" }, "failure_reason_code": { "type": [ "string", "null" - ], - "required": true + ] }, "failure_reason": { "type": [ "string", "null" - ], - "required": true + ] + }, + "transaction_number": { + "type": "string", + "pattern": "W[0-9]{3}-[0-9]{3}-[0-9]{4}" }, "meta": { - "type": "object", - "required": true + "type": "object" }, "links": { "type": "object", "properties": { "source": { "type": "string", - "pattern": "(CC|BA)[a-zA-Z0-9]{16,32}", - "required": true + "pattern": "(CC|BA)[a-zA-Z0-9]{16,32}" }, "customer": { "type": [ "null", "string" ], - "pattern": "(CU|AC)[a-zA-Z0-9]{16,32}", - "required": true + "pattern": "(CU|AC)[a-zA-Z0-9]{16,32}" }, "order": { "type": [ "null", "string" ], - "pattern": "OR[a-zA-Z0-9]{16,32}", - "required": true + "pattern": "OR[a-zA-Z0-9]{16,32}" } }, - "required": true, + "required": [ + "source", + "customer", + "order" + ], "additionalProperties": false } }, - "additionalProperties": false -} + "additionalProperties": false, + "required": [ + "id", + "href", + "created_at", + "updated_at", + "amount", + "currency", + "appears_on_statement_as", + "description", + "status", + "failure_reason_code", + "transaction_number", + "meta", + "links" + ] +} \ No newline at end of file diff --git a/fixtures/debits.json b/fixtures/debits.json new file mode 100644 index 0000000..55a7093 --- /dev/null +++ b/fixtures/debits.json @@ -0,0 +1,57 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "links": { + "type": "object", + "properties": { + "debits.refunds": { + "type": "string", + "format": "uri", + "pattern": "/debits/{debits.id}/refunds" + }, + "debits.customer": { + "type": "string", + "format": "uri", + "pattern": "/customers/{debits.customer}" + }, + "debits.order": { + "type": "string", + "format": "uri", + "pattern": "/orders/{debits.order}" + }, + "debits.source": { + "type": "string", + "format": "uri", + "pattern": "/resources/{debits.source}" + }, + "debits.events": { + "type": "string", + "format": "uri", + "pattern": "/debits/{debits.id}/events" + } + }, + "required": [ + "debits.refunds", + "debits.customer", + "debits.order", + "debits.source", + "debits.events" + ] + }, + "meta": { + "type": "object" + }, + "debits": { + "items": { + "$ref": "_models/debit.json" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true + } + }, + "require": [ + "debits" + ] +} diff --git a/lib/balanced/tiny_client.rb b/lib/balanced/tiny_client.rb index beaac16..edd5a9c 100644 --- a/lib/balanced/tiny_client.rb +++ b/lib/balanced/tiny_client.rb @@ -37,6 +37,10 @@ def get(endpoint) verb 'GET', endpoint end + def delete(endpoint) + verb 'DELETE', endpoint + end + def add_response(response) @responses << response end @@ -87,10 +91,16 @@ def add_hydrate(key, value) def validate(against) file_name = File.join('fixtures', "#{against}.json") - if File.exists? file_name and not against.is_a? Hash - JSON::Validator.validate!(file_name, last_body) - else - JSON::Validator.validate!(against, last_body) + begin + if File.exists? file_name and not against.is_a? Hash + JSON::Validator.validate!(file_name, last_body) + else + JSON::Validator.validate!(against, last_body) + end + rescue JSON::Schema::ValidationError => e + puts JSON.pretty_generate last_body + puts e.message + raise e end end From e7eaf4501f02976c7671220ef8b4f39fbd4ebf34 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Tue, 17 Dec 2013 11:39:57 -0800 Subject: [PATCH 032/154] feature: Retrieving debits for a card --- features/debits.feature | 14 ++++++++++++++ features/step_definitions/cards.rb | 15 --------------- features/step_definitions/debits.rb | 6 ++++++ features/step_definitions/http_steps.rb | 10 +++++++++- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/features/debits.feature b/features/debits.feature index b7a60ed..68736e8 100644 --- a/features/debits.feature +++ b/features/debits.feature @@ -19,3 +19,17 @@ Feature: Debit a card or bank account "amount": 20000 } """ + + Scenario: Retrieving debits for a card + Given I have debited a card + When I GET to /cards/:card_id/debits + Then I should get a 200 OK status code + And the response is valid according to the "debits" schema + And the fields on these debits match: + """ + { + "links": { + "source": ":card_id" + } + } + """ diff --git a/features/step_definitions/cards.rb b/features/step_definitions/cards.rb index 2bda115..48b8051 100644 --- a/features/step_definitions/cards.rb +++ b/features/step_definitions/cards.rb @@ -17,18 +17,3 @@ When(/^I GET to \/cards\/:card_id giving the card_id$/) do @client.get("/cards/#{@card_id}") end - -# When(/^I DELETE to \/cards\/:card_id giving the card_id$/) do -# @client.delete( -# # options = { -# # headers: { -# # "Accept" => $accept_header, -# # }, -# # basic_auth: { -# # username: $api_secret, -# # password: "", -# # } -# # } -# # response = HTTParty.delete("#{@client.root_url}/cards/#{@card_id}", options) -# # @client.add_response(response) -# end diff --git a/features/step_definitions/debits.rb b/features/step_definitions/debits.rb index e69de29..0bf2d71 100644 --- a/features/step_definitions/debits.rb +++ b/features/step_definitions/debits.rb @@ -0,0 +1,6 @@ +Given(/^I have debited a card$/) do + step "I have tokenized a card" + @client.post("/cards/#{@card_id}/debits", { + 'amount' => 1234 + }) +end diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 0364311..96a5a1f 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -65,7 +65,15 @@ def checker(from, of, nesting) end Then(/^the fields on this (.*) match:$/) do |resource, against| - checker JSON.parse(against), @client["#{resource}s"], '' + checker JSON.parse(@client.hydrater against), @client["#{resource}s"], '' +end + + +Then(/^the fields on these (.*) match:$/) do |resource, against| + against = JSON.parse(@client.hydrater against) + @client.last_body[resource].each do |body| + checker against, body, '' + end end Then(/^there should be more than two (.*) paged$/) do |name| From 26878a3aca38a30a1be9aafe7f4cb52a6688b371 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 17 Dec 2013 18:49:52 +0000 Subject: [PATCH 033/154] Implement PUT in the client. --- lib/balanced/tiny_client.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/balanced/tiny_client.rb b/lib/balanced/tiny_client.rb index edd5a9c..15b4703 100644 --- a/lib/balanced/tiny_client.rb +++ b/lib/balanced/tiny_client.rb @@ -33,6 +33,24 @@ def post(endpoint, body) response end + def put(endpoint, body) + body = JSON.parse(body) if body.is_a? String + options = { + headers: { + 'Accept' => @accept_header, + }, + body: body, + basic_auth: { + username: @api_secret, + password: '', + } + } + + response = HTTParty.put("#{@root_url}#{endpoint}", options) + @responses << response + response + end + def get(endpoint) verb 'GET', endpoint end From 647f6313ac4405715adc760a1a9190db9868625c Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 17 Dec 2013 18:50:03 +0000 Subject: [PATCH 034/154] New feature: update card --- features/cards.feature | 14 ++++++++++++++ features/step_definitions/cards.rb | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/features/cards.feature b/features/cards.feature index db8e6d8..06dc4fc 100644 --- a/features/cards.feature +++ b/features/cards.feature @@ -47,3 +47,17 @@ Feature: Tokenize a credit card When I DELETE to /cards/:card_id Then I should get a 204 OK status code And there should be no response body + + @failing + Scenario: Update a card + Given I have tokenized a card + When I PUT to /cards/:card_id giving the card_id, with the JSON API body: + """ + { + "number": "4111111111111111", + "expiration_month": "12", + "expiration_year": 2016 + } + """ + Then I should get a 200 OK status code + And the response is valid according to the "cards" schema diff --git a/features/step_definitions/cards.rb b/features/step_definitions/cards.rb index 48b8051..9be0519 100644 --- a/features/step_definitions/cards.rb +++ b/features/step_definitions/cards.rb @@ -17,3 +17,21 @@ When(/^I GET to \/cards\/:card_id giving the card_id$/) do @client.get("/cards/#{@card_id}") end + +When(/^I DELETE to \/cards\/:card_id giving the card_id$/) do + options = { + headers: { + "Accept" => $accept_header, + }, + basic_auth: { + username: $api_secret, + password: "", + } + } + response = HTTParty.delete("#{@client.root_url}/cards/#{@card_id}", options) + @client.add_response(response) +end + +When(/^I PUT to \/cards\/:card_id giving the card_id, with the JSON API body:$/) do |body| + @client.put("/cards/#{@card_id}", body) +end From ea7a736e2f31616d371b85d08f8e696988827c63 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 17 Dec 2013 19:22:26 +0000 Subject: [PATCH 035/154] Fix broken callback scenario. Thanks for setting me straight, @mjallday --- features/callbacks.feature | 1 - features/step_definitions/callbacks.rb | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/features/callbacks.feature b/features/callbacks.feature index 4f984d2..910a5de 100644 --- a/features/callbacks.feature +++ b/features/callbacks.feature @@ -22,7 +22,6 @@ Feature: Callbacks Then I should get a 200 OK status code # TODO: Callback schema? - @failing Scenario: Remove a callback Given I have created a callback When I DELETE to /callbacks/:callback_id giving the callback_id diff --git a/features/step_definitions/callbacks.rb b/features/step_definitions/callbacks.rb index 2c68911..24d41c1 100644 --- a/features/step_definitions/callbacks.rb +++ b/features/step_definitions/callbacks.rb @@ -1,6 +1,6 @@ Given(/^I have created a callback$/) do - @client.post('/callbacks', {}) - @callback_id = @client['id'] + @client.post('/callbacks', {url: "http://example.com/callback"}) + @callback_id = @client.last_body['callbacks'].first['id'] end Given(/^I have created more than one callback$/) do From 7c2c8e69731ef674afdec7a8214b1b2a8eba6766 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Tue, 17 Dec 2013 12:01:26 -0800 Subject: [PATCH 036/154] feature: debit a customer --- features/customers.feature | 6 +++--- features/debits.feature | 23 +++++++++++++++++++++++ features/step_definitions/customers.rb | 7 ++++--- features/step_definitions/debits.rb | 11 +++++++++++ 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/features/customers.feature b/features/customers.feature index d3652ea..bbf7841 100644 --- a/features/customers.feature +++ b/features/customers.feature @@ -22,19 +22,19 @@ Feature: Customers """ Scenario: Get a customer - Given I have created a Customer + Given I have created a customer When I GET to /customers/:customer_id giving the customer_id Then I should get a 200 OK status code And the response is valid according to the "customers" schema Scenario: List all customers - Given I have created more than one Customer + Given I have created more than one customer When I GET to /customers Then I should get a 200 OK status code And there should be more than two customers paged Scenario: Remove a customer - Given I have created a Customer + Given I have created a customer When I DELETE to /customers/:customer_id giving the customer_id Then I should get a 204 OK status code And there should be no response body diff --git a/features/debits.feature b/features/debits.feature index 68736e8..5dbe569 100644 --- a/features/debits.feature +++ b/features/debits.feature @@ -33,3 +33,26 @@ Feature: Debit a card or bank account } } """ + + Scenario: Debiting a customer + If the debit is done directly on the customer resource + then that customers default "funding source" will be used + when preforming the debit + Given I have a customer with a card + When I POST to /customers/:customer_id/debits with the JSON API body: + """ + { + "amount": 2000 + } + """ + Then I should get a 201 Created status code + And the response is valid according to the "debits" schema + And the fields on this debit match: + """ + { + "amount": 2000, + "links": { + "customer": ":customer_id" + } + } + """ diff --git a/features/step_definitions/customers.rb b/features/step_definitions/customers.rb index 9c26a9d..781841d 100644 --- a/features/step_definitions/customers.rb +++ b/features/step_definitions/customers.rb @@ -1,10 +1,11 @@ -Given(/^I have created a Customer$/) do +Given(/^I have created a customer$/) do @client.post('/customers', {}) @customer_id = @client['id'] + @client.add_hydrate :customer_id, @customer_id end -Given(/^I have created more than one Customer$/) do - 2.times { step "I have created a Customer" } +Given(/^I have created more than one customer$/) do + 2.times { step "I have created a customer" } end When(/^I GET to \/customers\/:customer_id giving the customer_id$/) do diff --git a/features/step_definitions/debits.rb b/features/step_definitions/debits.rb index 0bf2d71..aa18818 100644 --- a/features/step_definitions/debits.rb +++ b/features/step_definitions/debits.rb @@ -4,3 +4,14 @@ 'amount' => 1234 }) end + +Given(/^I have a customer with a card$/) do + step "I have created a customer" + step "I have tokenized a card" + @client.put("/cards/#{@card_id}", { + 'links' => { + 'customer' => @customer_id + } + }) + +end From 72b55460802afba1337e4fad6d12b7dcb57e9709 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 17 Dec 2013 19:53:06 +0000 Subject: [PATCH 037/154] new feature: debit a card --- features/cards.feature | 12 ++++++++++++ features/step_definitions/cards.rb | 4 ++++ fixtures/_models/debit.json | 2 +- lib/balanced/tiny_client.rb | 16 +++++----------- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/features/cards.feature b/features/cards.feature index 06dc4fc..d5fdcc4 100644 --- a/features/cards.feature +++ b/features/cards.feature @@ -61,3 +61,15 @@ Feature: Tokenize a credit card """ Then I should get a 200 OK status code And the response is valid according to the "cards" schema + + @failing + Scenario: Debit a card + Given I have tokenized a card + When I POST to /cards/:card_id/debits giving the card_id, with the JSON API body: + """ + { + "amount": "1" + } + """ + Then I should get a 201 Created status code + And the response is valid according to the "_models/debit" schema diff --git a/features/step_definitions/cards.rb b/features/step_definitions/cards.rb index 9be0519..3e1d9ac 100644 --- a/features/step_definitions/cards.rb +++ b/features/step_definitions/cards.rb @@ -35,3 +35,7 @@ When(/^I PUT to \/cards\/:card_id giving the card_id, with the JSON API body:$/) do |body| @client.put("/cards/#{@card_id}", body) end + +When(/^I POST to \/cards\/:card_id\/debits giving the card_id, with the JSON API body:$/) do |body| + @client.post("/cards/#{@card_id}/debits", body) +end diff --git a/fixtures/_models/debit.json b/fixtures/_models/debit.json index 561500f..bcdc5aa 100644 --- a/fixtures/_models/debit.json +++ b/fixtures/_models/debit.json @@ -106,4 +106,4 @@ "meta", "links" ] -} \ No newline at end of file +} diff --git a/lib/balanced/tiny_client.rb b/lib/balanced/tiny_client.rb index 15b4703..adaeffc 100644 --- a/lib/balanced/tiny_client.rb +++ b/lib/balanced/tiny_client.rb @@ -108,17 +108,11 @@ def add_hydrate(key, value) end def validate(against) - file_name = File.join('fixtures', "#{against}.json") - begin - if File.exists? file_name and not against.is_a? Hash - JSON::Validator.validate!(file_name, last_body) - else - JSON::Validator.validate!(against, last_body) - end - rescue JSON::Schema::ValidationError => e - puts JSON.pretty_generate last_body - puts e.message - raise e + file_name = File.join(File.dirname(__FILE__), "../..", 'fixtures', "#{against}.json") + if File.exists?(file_name) and not against.is_a? Hash + JSON::Validator.validate!(file_name, last_body) + else + JSON::Validator.validate!(against, last_body) end end From 492b2f85b19e94455182bca2744a88ac5e72a7fc Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Tue, 17 Dec 2013 12:22:02 -0800 Subject: [PATCH 038/154] spacing is hard --- features/debits.feature | 42 ++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/features/debits.feature b/features/debits.feature index 5dbe569..952e1b5 100644 --- a/features/debits.feature +++ b/features/debits.feature @@ -34,25 +34,25 @@ Feature: Debit a card or bank account } """ - Scenario: Debiting a customer - If the debit is done directly on the customer resource - then that customers default "funding source" will be used - when preforming the debit - Given I have a customer with a card - When I POST to /customers/:customer_id/debits with the JSON API body: - """ - { - "amount": 2000 - } - """ - Then I should get a 201 Created status code - And the response is valid according to the "debits" schema - And the fields on this debit match: - """ - { - "amount": 2000, - "links": { - "customer": ":customer_id" - } + Scenario: Debiting a customer + If the debit is done directly on the customer resource + then that customers default "funding source" will be used + when preforming the debit. + Given I have a customer with a card + When I POST to /customers/:customer_id/debits with the JSON API body: + """ + { + "amount": 2000 + } + """ + Then I should get a 201 Created status code + And the response is valid according to the "debits" schema + And the fields on this debit match: + """ + { + "amount": 2000, + "links": { + "customer": ":customer_id" } - """ + } + """ From b6cfe2b9880b384ffd4d4a52086ddf6cf7a63e60 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Tue, 17 Dec 2013 13:13:46 -0800 Subject: [PATCH 039/154] bank accounts feature failing as address is not coming back in response --- features/bank_accounts.feature | 24 +++++++++ features/step_definitions/http_steps.rb | 2 +- fixtures/_models/bank_account.json | 70 +++++++++++++------------ fixtures/bank_account_tokens.json | 26 +++++++++ fixtures/bank_accounts.json | 57 ++++++++++++++++++++ 5 files changed, 145 insertions(+), 34 deletions(-) create mode 100644 features/bank_accounts.feature create mode 100644 fixtures/bank_account_tokens.json create mode 100644 fixtures/bank_accounts.json diff --git a/features/bank_accounts.feature b/features/bank_accounts.feature new file mode 100644 index 0000000..445d6a4 --- /dev/null +++ b/features/bank_accounts.feature @@ -0,0 +1,24 @@ +Feature: Bank accounts + The bank account resource respresent a funding source or + destiation that is backed by a bank account. Marketplaces + are able to credit out to bank accounts without doing any verifications + but to debit from a bank account, micro deposit verifications are + required. + + @failing + Scenario: Tokenize a bank account + When I POST to /bank_accounts without my secret key with the JSON API body: + """ + { + "name": "That guy over there", + "routing_number": "321174851", + "account_number": "9900000001", + "account_type": "checking" + } + """ + Then I should get a 201 Created status code + And the response is valid according to the "bank_account_tokens" schema + + When I GET "bank_accounts.href" from the previous response + Then I should get a 200 OK status code + And the response is valid according to the "bank_accounts" schema diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 96a5a1f..d5d4db1 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -2,7 +2,7 @@ @client.verb(verb, @client.hydrater(url)) end -When(/^I POST to (\/\S*) without my secret key with the JSON API body:$/) do |url, body| +When(/^I POST to (\/.*) without my secret key with the JSON API body:$/) do |url, body| # use for tokenizing cards and bank accounts options = { headers: { diff --git a/fixtures/_models/bank_account.json b/fixtures/_models/bank_account.json index f925162..559d0f4 100644 --- a/fixtures/_models/bank_account.json +++ b/fixtures/_models/bank_account.json @@ -4,72 +4,58 @@ "properties": { "id": { "type": "string", - "required": true, "pattern": "BA[a-zA-Z0-9]{16,32}" }, "href": { "type": "string", - "format": "uri", - "required": true + "format": "uri" }, "created_at": { "type": "string", - "format": "date-time", - "required": true + "format": "date-time" }, "updated_at": { "type": "string", - "format": "date-time", - "required": true + "format": "date-time" }, "name": { "type": [ "string", "null" - ], - "required": true + ] }, "account_number": { "type": "string", - "pattern": "x*[0-9]{4}", - "required": true + "pattern": "x*[0-9]{4}" }, "routing_number": { "type": "string", - "pattern": "[0-9]{9}", - "required": true + "pattern": "[0-9]{9}" }, "account_type": { "type": "string", "enum": [ "checking", "savings" - ], - "required": true + ] }, "bank_name": { - "type": "string", - "required": true + "type": "string" }, "address": { - "$ref": "address.json", - "required": true + "$ref": "address.json" }, "fingerprint": { - "type": "string", - "required": true + "type": "string" }, "can_debit": { - "type": "boolean", - "required": true + "type": "boolean" }, "can_credit": { - "type": "boolean", - "required": true + "type": "boolean" }, "meta": { - "type": "object", - "required": true + "type": "object" }, "links": { "type": "object", @@ -79,21 +65,39 @@ "null", "string" ], - "pattern": "(CU|AC)[a-zA-Z0-9]{16,32}", - "required": true + "pattern": "(CU|AC)[a-zA-Z0-9]{16,32}" }, "bank_account_verification": { "type": [ "null", "string" ], - "pattern": "BZ[a-zA-Z0-9]{16,32}", - "required": true + "pattern": "BZ[a-zA-Z0-9]{16,32}" } }, - "required": true, + "required": [ + "customer", + "bank_account_verification" + ], "additionalProperties": false } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "id", + "href", + "created_at", + "updated_at", + "name", + "account_number", + "routing_number", + "account_type", + "bank_name", + "address", + "fingerprint", + "can_debit", + "can_credit", + "meta", + "links" + ] } \ No newline at end of file diff --git a/fixtures/bank_account_tokens.json b/fixtures/bank_account_tokens.json new file mode 100644 index 0000000..689029c --- /dev/null +++ b/fixtures/bank_account_tokens.json @@ -0,0 +1,26 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Bank Account tokenization response", + "description": "When using balanced.js or POSTing without an API key to create a Bank Account, this is the schema that is followed instead of the cards.json or bank_accounts.json schema.", + "type": "object", + "properties": { + "links": { + "type": "object" + }, + "meta": { + "type": "object" + }, + "bank_accounts": { + "items": { + "$ref": "_models/token.json" + }, + "type": "array", + "minItems": 1, + "maxItems": 1, + "uniqueItems": true + } + }, + "required": [ + "bank_accounts" + ] +} \ No newline at end of file diff --git a/fixtures/bank_accounts.json b/fixtures/bank_accounts.json new file mode 100644 index 0000000..91fe3fe --- /dev/null +++ b/fixtures/bank_accounts.json @@ -0,0 +1,57 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "links": { + "type": "object", + "properties": { + "bank_accounts.bank_account_verifications": { + "type": "string", + "format": "uri", + "pattern": "/bank_accounts/{bank_accounts.id}/verifications" + }, + "bank_accounts.credits": { + "type": "string", + "format": "uri", + "pattern": "/bank_accounts/{bank_accounts.id}/credits" + }, + "bank_accounts.debits": { + "type": "string", + "format": "uri", + "pattern": "/bank_accounts/{bank_accounts.id}/debits" + }, + "bank_accounts.customer": { + "type": "string", + "format": "uri", + "pattern": "/customers/{bank_accounts.customer}" + }, + "bank_accounts.bank_account_verification": { + "type": "string", + "format": "uri", + "pattern": "/verifications/{bank_accounts.bank_account_verification}" + } + }, + "required": [ + "bank_accounts.bank_account_verifications", + "bank_accounts.credits", + "bank_accounts.debits", + "bank_accounts.customer", + "bank_accounts.bank_account_verification" + ] + }, + "meta": { + "type": "object" + }, + "bank_accounts": { + "items": { + "$ref": "_models/bank_account.json" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true + } + }, + "required": [ + "bank_accounts" + ] +} From ca0e231aed3c57980c4ddb783d3b8ac56de9ec45 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Tue, 17 Dec 2013 13:58:42 -0800 Subject: [PATCH 040/154] credit bank account feature --- features/credits.feature | 17 +++++ features/step_definitions/bank_accounts.rb | 10 +++ features/step_definitions/cards.rb | 7 +++ features/step_definitions/http_steps.rb | 5 +- fixtures/_models/credit.json | 72 ++++++++++++---------- fixtures/credits.json | 64 +++++++++++++++++++ 6 files changed, 142 insertions(+), 33 deletions(-) create mode 100644 features/credits.feature create mode 100644 features/step_definitions/bank_accounts.rb create mode 100644 fixtures/credits.json diff --git a/features/credits.feature b/features/credits.feature new file mode 100644 index 0000000..38bf454 --- /dev/null +++ b/features/credits.feature @@ -0,0 +1,17 @@ +Feature: Credits + Credit is the action of moving money out of the marketplace. + The money will come from either the marketplace's escrow balance + or an order and can be sent to a bank account. If sent to a customer + then that customer's default bank account will be used instead. + + Scenario: Credit a bank account + Given I have sufficent funds in my marketplaces + And I have tokenized a bank account + When I POST to /bank_accounts/:bank_account_id/credits with the JSON API body: + """ + { + "amount": 1234 + } + """ + And I should get a 201 Created status code + And the response is valid according to the "credits" schema diff --git a/features/step_definitions/bank_accounts.rb b/features/step_definitions/bank_accounts.rb new file mode 100644 index 0000000..4d40cb3 --- /dev/null +++ b/features/step_definitions/bank_accounts.rb @@ -0,0 +1,10 @@ +Given(/^I have tokenized a bank account$/) do + @client.post('/bank_accounts', { + name: "Hhenry Ford", + routing_number: "321174851", + account_number: "9900000001", + account_type: "checking", + }) + @bank_account_id = @client['bank_accounts']['id'] + @client.add_hydrate(:bank_account_id, @bank_account_id) +end diff --git a/features/step_definitions/cards.rb b/features/step_definitions/cards.rb index 3e1d9ac..0a551aa 100644 --- a/features/step_definitions/cards.rb +++ b/features/step_definitions/cards.rb @@ -39,3 +39,10 @@ When(/^I POST to \/cards\/:card_id\/debits giving the card_id, with the JSON API body:$/) do |body| @client.post("/cards/#{@card_id}/debits", body) end + +Given(/^I have sufficent funds in my marketplaces$/) do + step 'I have tokenized a card' + @client.post("/cards/#{@card_id}/debits", { + amount: 50000 + }) +end diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index d5d4db1..ecafa31 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -35,6 +35,10 @@ @client.post(@client.hydrater(url), body) end +When(/^I PUT to (\/\S*) with the JSON API body:$/) do |url, body| + @client.put(@client.hydrater(url), body) +end + require 'json-schema' Then(/^the response has this schema:$/) do |schema| @client.validate(schema) @@ -68,7 +72,6 @@ def checker(from, of, nesting) checker JSON.parse(@client.hydrater against), @client["#{resource}s"], '' end - Then(/^the fields on these (.*) match:$/) do |resource, against| against = JSON.parse(@client.hydrater against) @client.last_body[resource].each do |body| diff --git a/fixtures/_models/credit.json b/fixtures/_models/credit.json index f1d4e27..884a99c 100644 --- a/fixtures/_models/credit.json +++ b/fixtures/_models/credit.json @@ -4,69 +4,61 @@ "properties": { "id": { "type": "string", - "required": true, "pattern": "CR[a-zA-Z0-9]{16,32}" }, "href": { "type": "string", - "format": "uri", - "required": true + "format": "uri" }, "created_at": { "type": "string", - "format": "date-time", - "required": true + "format": "date-time" }, "updated_at": { "type": "string", - "format": "date-time", - "required": true + "format": "date-time" }, "amount": { "type": "integer", - "minimum": 1, - "required": true + "minimum": 1 }, "currency": { "type": "string", "enum": [ "USD" - ], - "required": true + ] }, "appears_on_statement_as": { "type": "string", - "pattern": "[a-zA-Z0-9 \\.\\-]{1,18}", - "required": true + "pattern": "[a-zA-Z0-9 \\.\\-]{1,18}" }, "description": { "type": [ "string", "null" - ], - "required": true + ] }, "status": { - "$ref": "status.json", - "required": true + "$ref": "status.json" }, "failure_reason_code": { "type": [ "string", "null" - ], - "required": true + ] }, "failure_reason": { "type": [ "string", "null" - ], - "required": true + ] }, + "transaction_number": { + "type": "string", + "pattern": "CR[0-9]{3}-[0-9]{3}-[0-9]{4}" + }, "meta": { - "type": "object", - "required": true + "type": "object" }, "links": { "type": "object", @@ -76,26 +68,42 @@ "null", "string" ], - "pattern": "(CU|AC)[a-zA-Z0-9]{16,32}", - "required": true + "pattern": "(CU|AC)[a-zA-Z0-9]{16,32}" }, "destination": { "type": "string", - "pattern": "BA[a-zA-Z0-9]{16,32}", - "required": true + "pattern": "BA[a-zA-Z0-9]{16,32}" }, "order": { "type": [ "null", "string" ], - "pattern": "OR[a-zA-Z0-9]{16,32}", - "required": true + "pattern": "OR[a-zA-Z0-9]{16,32}" } }, - "required": true, + "required": [ + "customer", + "destination", + "order" + ], "additionalProperties": false } }, - "additionalProperties": false -} \ No newline at end of file + "additionalProperties": false, + "required": [ + "id", + "href", + "created_at", + "updated_at", + "amount", + "currency", + "appears_on_statement_as", + "description", + "status", + "failure_reason_code", + "failure_reason", + "meta", + "links" + ] +} diff --git a/fixtures/credits.json b/fixtures/credits.json new file mode 100644 index 0000000..800d5b7 --- /dev/null +++ b/fixtures/credits.json @@ -0,0 +1,64 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "links": { + "type": "object", + "properties": { + "credits.reversals": { + "type": "string", + "format": "uri", + "pattern": "/credits/{credits.id}/reversals", + "required": true + }, + "credits.customer": { + "type": "string", + "format": "uri", + "pattern": "/customers/{credits.customer}", + "required": true + }, + "credits.order": { + "type": "string", + "format": "uri", + "pattern": "/orders/{credits.order}", + "required": true + }, + "credits.destination": { + "type": "string", + "format": "uri", + "pattern": "/resources/{credits.destination}", + "required": true + }, + "credits.events": { + "type": "string", + "format": "uri", + "pattern": "/credits/{credits.id}/events", + "required": true + } + }, + "required": [ + "credits.reversals", + "credits.customer", + "credits.order", + "credits.destination", + "credits.events" + ] + }, + "meta": { + "type": "object", + "required": false + }, + "credits": { + "items": { + "$ref": "_models/credit.json" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true, + "required": true + } + }, + "required": [ + "credits" + ] +} \ No newline at end of file From 125f31ba88bf29f778b166065a526deaccce1a76 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Tue, 17 Dec 2013 14:34:55 -0800 Subject: [PATCH 041/154] basic crud for bank accounts --- features/bank_accounts.feature | 35 ++++++++++++++++++++++ features/cards.feature | 2 +- features/step_definitions/bank_accounts.rb | 5 ++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/features/bank_accounts.feature b/features/bank_accounts.feature index 445d6a4..0dd7e28 100644 --- a/features/bank_accounts.feature +++ b/features/bank_accounts.feature @@ -22,3 +22,38 @@ Feature: Bank accounts When I GET "bank_accounts.href" from the previous response Then I should get a 200 OK status code And the response is valid according to the "bank_accounts" schema + + @failing + Scenario: Retrieve a bank account + Given I have tokenized a bank account + When I GET to /bank_accounts/:bank_account_id + Then I should get a 200 OK status code + And the response is valid according to the "bank_accounts" schema + + @failing + Scenario: List bank accounts + Given I have tokenized more than one bank account + When I GET to /bank_accounts + Then I should get a 200 OK status code + And the response is valid according to the "bank_accounts" schema + + @failing + Scenario: Unstore a bank account + Given I have tokenized a bank account + When I DELETE to /bank_accounts/:bank_account_id + Then I should get a 204 OK status code + And there should be no response body + + @failing + Scenario: Update a bank account + Given I have tokenized a bank account + When I PUT to /bank_accounts/:bank_account_id with the JSON API body: + """ + { + "meta": { + "random": "hello world" + } + } + """ + Then I should get a 200 OK status code + And the response is valid according to the "bank_accounts" schema diff --git a/features/cards.feature b/features/cards.feature index d5fdcc4..5d66b90 100644 --- a/features/cards.feature +++ b/features/cards.feature @@ -36,7 +36,7 @@ Feature: Tokenize a credit card And the response is valid according to the "cards" schema @failing - Scenario: List all cards + Scenario: List cards Given I have tokenized more than one card When I GET to /cards Then I should get a 200 OK status code diff --git a/features/step_definitions/bank_accounts.rb b/features/step_definitions/bank_accounts.rb index 4d40cb3..8b11ea7 100644 --- a/features/step_definitions/bank_accounts.rb +++ b/features/step_definitions/bank_accounts.rb @@ -8,3 +8,8 @@ @bank_account_id = @client['bank_accounts']['id'] @client.add_hydrate(:bank_account_id, @bank_account_id) end + + +Given(/^I have tokenized more than one bank account$/) do + 2.times { step 'I have tokenized a bank account' } +end From 45654ef9026b98b2bc41e150e85c2aad9452795c Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Tue, 17 Dec 2013 14:49:52 -0800 Subject: [PATCH 042/154] bank account debit and credit scenarios --- features/bank_accounts.feature | 23 ++++++++++++++++++++++ features/credits.feature | 4 ++-- features/step_definitions/bank_accounts.rb | 10 ++++++++++ features/step_definitions/cards.rb | 2 +- 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/features/bank_accounts.feature b/features/bank_accounts.feature index 0dd7e28..e5b15b0 100644 --- a/features/bank_accounts.feature +++ b/features/bank_accounts.feature @@ -57,3 +57,26 @@ Feature: Bank accounts """ Then I should get a 200 OK status code And the response is valid according to the "bank_accounts" schema + + Scenario: Credit a bank account + Given I have sufficient funds in my marketplaces + And I have tokenized a bank account + When I POST to /bank_accounts/:bank_account_id/credits with the JSON API body: + """ + { + "amount": 1234 + } + """ + Then I should get a 201 Created status code + And the response is valid according to the "credits" schema + + Scenario: Debit a bank account + Given I have a verified bank account + When I POST to /bank_accounts/:bank_account_id/debits with the JSON API body: + """ + { + "amount": 1234 + } + """ + Then I should get a 201 Created status code + And the response is valid according to the "debits" schema diff --git a/features/credits.feature b/features/credits.feature index 38bf454..4275aab 100644 --- a/features/credits.feature +++ b/features/credits.feature @@ -5,7 +5,7 @@ Feature: Credits then that customer's default bank account will be used instead. Scenario: Credit a bank account - Given I have sufficent funds in my marketplaces + Given I have sufficient funds in my marketplaces And I have tokenized a bank account When I POST to /bank_accounts/:bank_account_id/credits with the JSON API body: """ @@ -13,5 +13,5 @@ Feature: Credits "amount": 1234 } """ - And I should get a 201 Created status code + Then I should get a 201 Created status code And the response is valid according to the "credits" schema diff --git a/features/step_definitions/bank_accounts.rb b/features/step_definitions/bank_accounts.rb index 8b11ea7..6f03d7e 100644 --- a/features/step_definitions/bank_accounts.rb +++ b/features/step_definitions/bank_accounts.rb @@ -13,3 +13,13 @@ Given(/^I have tokenized more than one bank account$/) do 2.times { step 'I have tokenized a bank account' } end + + +Given(/^I have a verified bank account$/) do + step 'I have tokenized a bank account' + @client.post("/bank_accounts/#{@bank_account_id}/verifications", {}) + @client.put(@client['bank_account_verifications']['href'], { + amount_1: 1, + amount_2: 1 + }) +end diff --git a/features/step_definitions/cards.rb b/features/step_definitions/cards.rb index 0a551aa..273e21e 100644 --- a/features/step_definitions/cards.rb +++ b/features/step_definitions/cards.rb @@ -40,7 +40,7 @@ @client.post("/cards/#{@card_id}/debits", body) end -Given(/^I have sufficent funds in my marketplaces$/) do +Given(/^I have sufficient funds in my marketplaces$/) do step 'I have tokenized a card' @client.post("/cards/#{@card_id}/debits", { amount: 50000 From 24c22bb7ebd11f0870f3ea58e3070343f2c13618 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 17 Dec 2013 22:26:04 +0000 Subject: [PATCH 043/154] Move features into rest We'll keep these CRUD ones here, and do more intersting cases in the main folder. --- features/{ => rest}/api_keys.feature | 0 features/{ => rest}/bank_accounts.feature | 0 features/{ => rest}/callbacks.feature | 0 features/{ => rest}/cards.feature | 0 features/{ => rest}/credits.feature | 0 features/{ => rest}/customers.feature | 0 features/{ => rest}/debits.feature | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename features/{ => rest}/api_keys.feature (100%) rename features/{ => rest}/bank_accounts.feature (100%) rename features/{ => rest}/callbacks.feature (100%) rename features/{ => rest}/cards.feature (100%) rename features/{ => rest}/credits.feature (100%) rename features/{ => rest}/customers.feature (100%) rename features/{ => rest}/debits.feature (100%) diff --git a/features/api_keys.feature b/features/rest/api_keys.feature similarity index 100% rename from features/api_keys.feature rename to features/rest/api_keys.feature diff --git a/features/bank_accounts.feature b/features/rest/bank_accounts.feature similarity index 100% rename from features/bank_accounts.feature rename to features/rest/bank_accounts.feature diff --git a/features/callbacks.feature b/features/rest/callbacks.feature similarity index 100% rename from features/callbacks.feature rename to features/rest/callbacks.feature diff --git a/features/cards.feature b/features/rest/cards.feature similarity index 100% rename from features/cards.feature rename to features/rest/cards.feature diff --git a/features/credits.feature b/features/rest/credits.feature similarity index 100% rename from features/credits.feature rename to features/rest/credits.feature diff --git a/features/customers.feature b/features/rest/customers.feature similarity index 100% rename from features/customers.feature rename to features/rest/customers.feature diff --git a/features/debits.feature b/features/rest/debits.feature similarity index 100% rename from features/debits.feature rename to features/rest/debits.feature From aaa5fc483284fdae373c16b533353f4f5391fb02 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Tue, 17 Dec 2013 15:29:38 -0800 Subject: [PATCH 044/154] bank account verification feature --- .../rest/bank_account_verifications.feature | 39 +++++++++++++++ features/step_definitions/bank_accounts.rb | 7 +++ features/step_definitions/http_steps.rb | 1 + .../_models/bank_account_verification.json | 48 ++++++++++--------- fixtures/bank_account_verifications.json | 33 +++++++++++++ 5 files changed, 106 insertions(+), 22 deletions(-) create mode 100644 features/rest/bank_account_verifications.feature create mode 100644 fixtures/bank_account_verifications.json diff --git a/features/rest/bank_account_verifications.feature b/features/rest/bank_account_verifications.feature new file mode 100644 index 0000000..682a31e --- /dev/null +++ b/features/rest/bank_account_verifications.feature @@ -0,0 +1,39 @@ +Feature: Bank account verifications + Bank account verifications are used when you want to debit from a bank account. + In a production marketplace, you will create a bank account verification, + at which point Balanced will create two micro deposists into your customers + bank account, the amounts will be less than one dollar. You can then verify + the bank account by asking your customer for the value of the micro deposists + and then submitting the values to Balanced. + + In a test marketplace, the micro deposit amounts will always be 1 cent + + Scenario: Creating a bank account verification + Given I have tokenized a bank account + When I POST to /bank_accounts/:bank_account_id/verifications + Then I should get a 201 Created status code + And the response is valid according to the "bank_account_verifications" schema + + Scenario: Get verification for a bank account + Given I have a bank account with a verification + When I GET to /bank_accounts/:bank_account_id/verifications + Then I should get a 200 OK status code + And the response is valid according to the "bank_account_verifications" schema + + Scenario: Confirm a verification + Given I have a bank account with a verification + When I PUT to /verifications/:bank_account_verification_id with the JSON API body: + """ + { + "amount_1": 1, + "amount_2": 1 + } + """ + Then I should get a 200 OK status code + And the response is valid according to the "bank_account_verifications" schema + And the fields on this bank_account_verification match: + """ + { + "verification_status": "succeeded" + } + """ diff --git a/features/step_definitions/bank_accounts.rb b/features/step_definitions/bank_accounts.rb index 6f03d7e..15ff4e1 100644 --- a/features/step_definitions/bank_accounts.rb +++ b/features/step_definitions/bank_accounts.rb @@ -23,3 +23,10 @@ amount_2: 1 }) end + +Given(/^I have a bank account with a verification$/) do + step 'I have tokenized a bank account' + @client.post("/bank_accounts/#{@bank_account_id}/verifications", {}) + @bank_account_verification_id = @client['bank_account_verifications']['id'] + @client.add_hydrate :bank_account_verification_id, @bank_account_verification_id +end diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index ecafa31..805639a 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -70,6 +70,7 @@ def checker(from, of, nesting) Then(/^the fields on this (.*) match:$/) do |resource, against| checker JSON.parse(@client.hydrater against), @client["#{resource}s"], '' + assert_equal @client.last_body["#{resource}s"].size, 1 end Then(/^the fields on these (.*) match:$/) do |resource, against| diff --git a/fixtures/_models/bank_account_verification.json b/fixtures/_models/bank_account_verification.json index 249fe75..b990672 100644 --- a/fixtures/_models/bank_account_verification.json +++ b/fixtures/_models/bank_account_verification.json @@ -4,60 +4,64 @@ "properties": { "id": { "type": "string", - "pattern": "BZ[a-zA-Z0-9]{16,32}", - "required": true + "pattern": "BZ[a-zA-Z0-9]{16,32}" }, "href": { "type": "string", - "format": "uri", - "required": true + "format": "uri" }, "created_at": { "type": "string", - "format": "date-time", - "required": true + "format": "date-time" }, "updated_at": { "type": "string", - "format": "date-time", - "required": true + "format": "date-time" }, "attempts": { "type": "integer", "minimum": 0, - "maximum": 3, - "required": true + "maximum": 3 }, "attempts_remaining": { "type": "integer", "minimum": 0, - "maximum": 3, - "required": true + "maximum": 3 }, "deposit_status": { - "$ref": "status.json", - "required": true + "$ref": "status.json" }, "verification_status": { - "$ref": "status.json", - "required": true + "$ref": "status.json" }, "meta": { - "type": "object", - "required": true + "type": "object" }, "links": { "type": "object", "properties": { "bank_account": { "type": "string", - "pattern": "BA[a-zA-Z0-9]{16,32}", - "required": true + "pattern": "BA[a-zA-Z0-9]{16,32}" } }, - "required": true, + "required": [ + "bank_account" + ], "additionalProperties": false } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "id", + "href", + "created_at", + "updated_at", + "attempts", + "attempts_remaining", + "deposit_status", + "verification_status", + "meta", + "links" + ] } \ No newline at end of file diff --git a/fixtures/bank_account_verifications.json b/fixtures/bank_account_verifications.json new file mode 100644 index 0000000..3aed7bc --- /dev/null +++ b/fixtures/bank_account_verifications.json @@ -0,0 +1,33 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "links": { + "type": "object", + "properties": { + "bank_account_verifications.bank_account": { + "type": "string", + "format": "uri", + "pattern": "/bank_accounts/{bank_account_verifications.bank_account}" + } + }, + "required": [ + "bank_account_verifications.bank_account" + ] + }, + "meta": { + "type": "object" + }, + "bank_account_verifications": { + "items": { + "$ref": "_models/bank_account_verification.json" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true + } + }, + "required": [ + "bank_account_verifications" + ] +} \ No newline at end of file From fe968e5927186b9c23455cee9b17bbd785d03f7d Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Tue, 17 Dec 2013 15:53:41 -0800 Subject: [PATCH 045/154] finishing debit scenarios and forcing json schema formatting --- features/rest/debits.feature | 33 +++++++++++++++++ features/step_definitions/debits.rb | 6 ++++ fixtures/_models/address.json | 2 +- fixtures/_models/card.json | 2 +- fixtures/_models/credit.json | 10 +++--- fixtures/_models/customer.json | 5 ++- fixtures/_models/order.json | 2 +- fixtures/_models/refund.json | 56 ++++++++++++++++------------- fixtures/_models/token.json | 4 +-- fixtures/bank_accounts.json | 2 +- fixtures/card_tokens.json | 2 +- fixtures/cards.json | 2 +- fixtures/customers.json | 2 +- fixtures/debits.json | 2 +- fixtures/refunds.json | 45 +++++++++++++++++++++++ 15 files changed, 133 insertions(+), 42 deletions(-) create mode 100644 fixtures/refunds.json diff --git a/features/rest/debits.feature b/features/rest/debits.feature index 952e1b5..86fbbd1 100644 --- a/features/rest/debits.feature +++ b/features/rest/debits.feature @@ -56,3 +56,36 @@ Feature: Debit a card or bank account } } """ + + Scenario: List debits + Given I have more than one debit + When I GET to /debits + Then I should get a 200 OK status code + And the response is valid according to the "debits" schema + + Scenario: Update a debit + Given I have debited a card + When I PUT to /debits/:debit_id with the JSON API body: + """ + { + "meta": { + "order.status": "shipped" + } + } + """ + Then I should get a 200 OK status code + And the response is valid according to the "debits" schema + And the fields on this debit match: + """ + { + "meta": { + "order.status": "shipped" + } + } + """ + + Scenario: Refund a debit + Given I have debited a card + When I POST to /debits/:debit_id/refunds + Then I should get a 201 Created status code + And the response is valid according to the "refunds" schema diff --git a/features/step_definitions/debits.rb b/features/step_definitions/debits.rb index aa18818..706f8cb 100644 --- a/features/step_definitions/debits.rb +++ b/features/step_definitions/debits.rb @@ -3,6 +3,8 @@ @client.post("/cards/#{@card_id}/debits", { 'amount' => 1234 }) + @debit_id = @client['debits']['id'] + @client.add_hydrate :debit_id, @debit_id end Given(/^I have a customer with a card$/) do @@ -15,3 +17,7 @@ }) end + +Given(/^I have more than one debit$/) do + 2.times { step 'I have debited a card' } +end diff --git a/fixtures/_models/address.json b/fixtures/_models/address.json index e523ff8..d8cfcc2 100644 --- a/fixtures/_models/address.json +++ b/fixtures/_models/address.json @@ -51,4 +51,4 @@ } }, "additionalProperties": false -} +} \ No newline at end of file diff --git a/fixtures/_models/card.json b/fixtures/_models/card.json index c78b0d8..769403c 100644 --- a/fixtures/_models/card.json +++ b/fixtures/_models/card.json @@ -207,4 +207,4 @@ } }, "additionalProperties": false -} +} \ No newline at end of file diff --git a/fixtures/_models/credit.json b/fixtures/_models/credit.json index 884a99c..e1b3827 100644 --- a/fixtures/_models/credit.json +++ b/fixtures/_models/credit.json @@ -53,10 +53,10 @@ "null" ] }, - "transaction_number": { - "type": "string", - "pattern": "CR[0-9]{3}-[0-9]{3}-[0-9]{4}" - }, + "transaction_number": { + "type": "string", + "pattern": "CR[0-9]{3}-[0-9]{3}-[0-9]{4}" + }, "meta": { "type": "object" }, @@ -106,4 +106,4 @@ "meta", "links" ] -} +} \ No newline at end of file diff --git a/fixtures/_models/customer.json b/fixtures/_models/customer.json index 5ad8461..11b4b72 100644 --- a/fixtures/_models/customer.json +++ b/fixtures/_models/customer.json @@ -36,7 +36,6 @@ "string" ], "format": "email" - }, "phone": { "type": [ @@ -79,7 +78,7 @@ "need-more-information", "underwritten", "rejected", - "no-match" + "no-match" ] }, "address": { @@ -132,4 +131,4 @@ "links" ], "additionalProperties": false -} +} \ No newline at end of file diff --git a/fixtures/_models/order.json b/fixtures/_models/order.json index 4b1cfd5..04e6e26 100644 --- a/fixtures/_models/order.json +++ b/fixtures/_models/order.json @@ -68,4 +68,4 @@ } }, "additionalProperties": false -} +} \ No newline at end of file diff --git a/fixtures/_models/refund.json b/fixtures/_models/refund.json index 37f7ad7..8dc42b3 100644 --- a/fixtures/_models/refund.json +++ b/fixtures/_models/refund.json @@ -4,71 +4,79 @@ "properties": { "id": { "type": "string", - "required": true, "pattern": "RF[a-zA-Z0-9]{16,32}" }, "href": { "type": "string", - "format": "uri", - "required": true + "format": "uri" }, "created_at": { "type": "string", - "format": "date-time", - "required": true + "format": "date-time" }, "updated_at": { "type": "string", - "format": "date-time", - "required": true + "format": "date-time" }, "amount": { "type": "integer", - "minimum": 1, - "required": true + "minimum": 1 }, "currency": { "type": "string", "enum": [ "USD" - ], - "required": true + ] }, "description": { "type": [ "string", "null" - ], - "required": true + ] }, "status": { - "$ref": "status.json", - "required": true + "$ref": "status.json" + }, + "transaction_number": { + "type": "string", + "pattern": "RF[0-9]{3}-[0-9]{3}-[0-9]{4}" }, "meta": { - "type": "object", - "required": true + "type": "object" }, "links": { "type": "object", "properties": { "debit": { "type": "string", - "pattern": "WD[a-zA-Z0-9]{16,32}", - "required": true + "pattern": "WD[a-zA-Z0-9]{16,32}" }, "order": { "type": [ "null", "string" ], - "pattern": "OR[a-zA-Z0-9]{16,32}", - "required": true + "pattern": "OR[a-zA-Z0-9]{16,32}" } }, - "required": true, + "required": [ + "debit", + "order" + ], "additionalProperties": false } }, - "additionalProperties": false -} + "additionalProperties": false, + "required": [ + "id", + "href", + "created_at", + "updated_at", + "amount", + "currency", + "description", + "status", + "meta", + "links" + ] +} \ No newline at end of file diff --git a/fixtures/_models/token.json b/fixtures/_models/token.json index 17d76ba..fd766c2 100644 --- a/fixtures/_models/token.json +++ b/fixtures/_models/token.json @@ -8,7 +8,7 @@ }, "href": { "type": "string", - "format": "uri" + "format": "uri" }, "links": { "type": "object", @@ -22,4 +22,4 @@ "links" ], "additionalProperties": false -} +} \ No newline at end of file diff --git a/fixtures/bank_accounts.json b/fixtures/bank_accounts.json index 91fe3fe..96def46 100644 --- a/fixtures/bank_accounts.json +++ b/fixtures/bank_accounts.json @@ -54,4 +54,4 @@ "required": [ "bank_accounts" ] -} +} \ No newline at end of file diff --git a/fixtures/card_tokens.json b/fixtures/card_tokens.json index 4eb01a5..bd40485 100644 --- a/fixtures/card_tokens.json +++ b/fixtures/card_tokens.json @@ -23,4 +23,4 @@ "required": [ "cards" ] -} +} \ No newline at end of file diff --git a/fixtures/cards.json b/fixtures/cards.json index 84e6d9f..4130b3f 100644 --- a/fixtures/cards.json +++ b/fixtures/cards.json @@ -41,4 +41,4 @@ "required": [ "cards" ] -} +} \ No newline at end of file diff --git a/fixtures/customers.json b/fixtures/customers.json index 531d599..818b8dd 100644 --- a/fixtures/customers.json +++ b/fixtures/customers.json @@ -79,4 +79,4 @@ "required": [ "customers" ] -} +} \ No newline at end of file diff --git a/fixtures/debits.json b/fixtures/debits.json index 55a7093..0e3d417 100644 --- a/fixtures/debits.json +++ b/fixtures/debits.json @@ -54,4 +54,4 @@ "require": [ "debits" ] -} +} \ No newline at end of file diff --git a/fixtures/refunds.json b/fixtures/refunds.json new file mode 100644 index 0000000..7831eb3 --- /dev/null +++ b/fixtures/refunds.json @@ -0,0 +1,45 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "links": { + "type": "object", + "properties": { + "refunds.debit": { + "type": "string", + "format": "uri", + "pattern": "/debits/{refunds.debit}" + }, + "refunds.order": { + "type": "string", + "format": "uri", + "pattern": "/orders/{refunds.order}" + }, + "refunds.events": { + "type": "string", + "format": "uri", + "pattern": "/refunds/{refunds.id}/events" + } + }, + "required": [ + "refunds.debit", + "refunds.order", + "refunds.events" + ] + }, + "meta": { + "type": "object" + }, + "refunds": { + "items": { + "$ref": "_models/refund.json" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true + } + }, + "required": [ + "refunds" + ] +} \ No newline at end of file From e52e3f921f6d3d18f99ef64bf97846fb85c69b41 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Tue, 17 Dec 2013 16:11:28 -0800 Subject: [PATCH 046/154] refund features --- features/rest/refunds.feature | 57 +++++++++++++++++++++++++++++ features/step_definitions/refund.rb | 6 +++ 2 files changed, 63 insertions(+) create mode 100644 features/rest/refunds.feature create mode 100644 features/step_definitions/refund.rb diff --git a/features/rest/refunds.feature b/features/rest/refunds.feature new file mode 100644 index 0000000..9fe8b0f --- /dev/null +++ b/features/rest/refunds.feature @@ -0,0 +1,57 @@ +Feature: Refunds + Refunds are used to cancel a debit. The marketplace can either refund the whole amount + of the debit or a partial amount of the debit using "amount" when creating the refund. + + Scenario: Creating a full refund + Given I have debited a card + When I POST to /debits/:debit_id/refunds + Then I should get a 201 Created status code + And the response is valid according to the "refunds" schema + + Scenario: Creating a partial refund + Given I have debited a card + When I POST to /debits/:debit_id/refunds with the JSON API body: + """ + { + "amount": 100 + } + """ + Then I should get a 201 Created status code + And the response is valid according to the "refunds" schema + And the fields on this refund match: + """ + { + "amount": 100 + } + """ + + Scenario: Listing refunds for a debit + Given I have created a refund for a debit + When I GET to /debits/:debit_id/refunds + Then I should get a 200 OK status code + And the response is valid according to the "refunds" schema + And the fields on these refunds match: + """ + { + "links": { + "debit": ":debit_id" + } + } + """ + + Scenario: Retrieving a refund + Given I have created a refund for a debit + When I GET to /refunds/:refund_id + Then I should get a 200 OK status code + And the response is valid according to the "refunds" schema + + Scenario: Update a refund + Given I have created a refund for a debit + When I PUT to /refunds/:refund_id with the JSON API body: + """ + { + "description": "The customer cancel the order" + } + """ + Then I should get a 200 OK status code + And the response is valid according to the "refunds" schema diff --git a/features/step_definitions/refund.rb b/features/step_definitions/refund.rb new file mode 100644 index 0000000..ff11d79 --- /dev/null +++ b/features/step_definitions/refund.rb @@ -0,0 +1,6 @@ +Given(/^I have created a refund for a debit$/) do + step 'I have debited a card' + @client.post("/debits/#{@debit_id}/refunds", {}) + @refund_id = @client['refunds']['id'] + @client.add_hydrate :refund_id, @refund_id +end From 6ecfbee67eba5104bcf472891b4808bda29fa10a Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Tue, 17 Dec 2013 16:54:22 -0800 Subject: [PATCH 047/154] customer add card/bank account --- features/rest/customers.feature | 46 +++++++++++++++++++++++++ features/step_definitions/http_steps.rb | 2 +- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/features/rest/customers.feature b/features/rest/customers.feature index bbf7841..2794f6b 100644 --- a/features/rest/customers.feature +++ b/features/rest/customers.feature @@ -38,3 +38,49 @@ Feature: Customers When I DELETE to /customers/:customer_id giving the customer_id Then I should get a 204 OK status code And there should be no response body + + @failing + Scenario: Add a card to a customer + Given I have tokenized a card + And I have created a customer + When I PUT to /cards/:card_id with the JSON API body: + """ + { + "links": { + "customer": ":customer_id" + } + } + """ + Then I should get a 200 OK status code + And the response is valid according to the "cards" schema + And the fields on this card match: + """ + { + "links": { + "customer": ":customer_id" + } + } + """ + + @failing + Scenario: Add a bank account to a customer + Given I have tokenized a bank account + And I have created a customer + When I PUT to /bank_accounts/:bank_account_id with the JSON API body: + """ + { + "links": { + "customer": ":customer_id" + } + } + """ + Then I should get a 200 OK status code + And the response is valid according to the "bank_accounts" schema + And the fields on this bank_account match: + """ + { + "links": { + "customer": ":customer_id" + } + } + """ diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 805639a..4a3f1e1 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -36,7 +36,7 @@ end When(/^I PUT to (\/\S*) with the JSON API body:$/) do |url, body| - @client.put(@client.hydrater(url), body) + @client.put(@client.hydrater(url), @client.hydrater(body)) end require 'json-schema' From b7e3b68c00fb37c5c489e9bd694b593ada3b2b9a Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Tue, 17 Dec 2013 17:42:02 -0800 Subject: [PATCH 048/154] credits features --- features/rest/bank_accounts.feature | 2 +- features/rest/credits.feature | 22 ++++++++++- features/rest/reversals.feature | 7 ++++ features/step_definitions/cards.rb | 2 +- features/step_definitions/credits.rb | 14 +++++++ fixtures/_models/reversal.json | 58 +++++++++++++++------------- fixtures/credits.json | 21 ++++------ fixtures/reversals.json | 48 +++++++++++++++++++++++ 8 files changed, 130 insertions(+), 44 deletions(-) create mode 100644 features/rest/reversals.feature create mode 100644 features/step_definitions/credits.rb create mode 100644 fixtures/reversals.json diff --git a/features/rest/bank_accounts.feature b/features/rest/bank_accounts.feature index e5b15b0..eac7ad0 100644 --- a/features/rest/bank_accounts.feature +++ b/features/rest/bank_accounts.feature @@ -59,7 +59,7 @@ Feature: Bank accounts And the response is valid according to the "bank_accounts" schema Scenario: Credit a bank account - Given I have sufficient funds in my marketplaces + Given I have sufficient funds in my marketplace And I have tokenized a bank account When I POST to /bank_accounts/:bank_account_id/credits with the JSON API body: """ diff --git a/features/rest/credits.feature b/features/rest/credits.feature index 4275aab..203caf0 100644 --- a/features/rest/credits.feature +++ b/features/rest/credits.feature @@ -5,7 +5,7 @@ Feature: Credits then that customer's default bank account will be used instead. Scenario: Credit a bank account - Given I have sufficient funds in my marketplaces + Given I have sufficient funds in my marketplace And I have tokenized a bank account When I POST to /bank_accounts/:bank_account_id/credits with the JSON API body: """ @@ -15,3 +15,23 @@ Feature: Credits """ Then I should get a 201 Created status code And the response is valid according to the "credits" schema + + Scenario: List credits + Given I have more than two credits + When I GET to /credits + Then I should get a 200 OK status code + And the response is valid according to the "credits" schema + + Scenario: Retrieving credits for a bank account + Given I have a bank account with a credit + When I GET to /bank_accounts/:bank_account_id/credits + Then I should get a 200 OK status code + And the response is valid according to the "credits" schema + And the fields on these credits match: + """ + { + "links": { + "destination": ":bank_account_id" + } + } + """ diff --git a/features/rest/reversals.feature b/features/rest/reversals.feature new file mode 100644 index 0000000..b4fd6ef --- /dev/null +++ b/features/rest/reversals.feature @@ -0,0 +1,7 @@ +Feature: Reversal + Reversals allow for canceling a credit to a bank account. + A reversals can be partial or the full amount. + + @failing + Scenario: Creating a full reversal + Given I have credit a bank account diff --git a/features/step_definitions/cards.rb b/features/step_definitions/cards.rb index 273e21e..a70b4ca 100644 --- a/features/step_definitions/cards.rb +++ b/features/step_definitions/cards.rb @@ -40,7 +40,7 @@ @client.post("/cards/#{@card_id}/debits", body) end -Given(/^I have sufficient funds in my marketplaces$/) do +Given(/^I have sufficient funds in my marketplace$/) do step 'I have tokenized a card' @client.post("/cards/#{@card_id}/debits", { amount: 50000 diff --git a/features/step_definitions/credits.rb b/features/step_definitions/credits.rb new file mode 100644 index 0000000..5b65401 --- /dev/null +++ b/features/step_definitions/credits.rb @@ -0,0 +1,14 @@ +Given(/^I have a bank account with a credit$/) do + step 'I have sufficient funds in my marketplace' + step 'I have tokenized a bank account' + @client.post("/bank_accounts/#{@bank_account_id}/credits", { + amount: 2000 + }) + @credit_id = @client['credits']['id'] + @client.add_hydrate :credit_id, @credit_id +end + + +Given(/^I have more than two credits$/) do + 2.times { step 'I have a bank account with a credit' } +end diff --git a/fixtures/_models/reversal.json b/fixtures/_models/reversal.json index cc38a12..0a7de21 100644 --- a/fixtures/_models/reversal.json +++ b/fixtures/_models/reversal.json @@ -4,85 +4,89 @@ "properties": { "id": { "type": "string", - "required": true, "pattern": "RV[a-zA-Z0-9]{16,32}" }, "href": { "type": "string", - "format": "uri", - "required": true + "format": "uri" }, "created_at": { "type": "string", - "format": "date-time", - "required": true + "format": "date-time" }, "updated_at": { "type": "string", - "format": "date-time", - "required": true + "format": "date-time" }, "amount": { "type": "integer", - "minimum": 1, - "required": true + "minimum": 1 }, "currency": { "type": "string", "enum": [ "USD" - ], - "required": true + ] }, "description": { "type": [ "string", "null" - ], - "required": true + ] }, "status": { - "$ref": "status.json", - "required": true + "$ref": "status.json" }, "failure_reason_code": { "type": [ "string", "null" - ], - "required": true + ] }, "failure_reason": { "type": [ "string", "null" - ], - "required": true + ] }, "meta": { - "type": "object", - "required": true + "type": "object" }, "links": { "type": "object", "properties": { "credit": { "type": "string", - "pattern": "CR[a-zA-Z0-9]{16,32}", - "required": true + "pattern": "CR[a-zA-Z0-9]{16,32}" }, "order": { "type": [ "null", "string" ], - "pattern": "OR[a-zA-Z0-9]{16,32}", - "required": true + "pattern": "OR[a-zA-Z0-9]{16,32}" } }, - "required": true, + "required": [ + "credit", + "order" + ], "additionalProperties": false } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "id", + "href", + "created_at", + "updated_at", + "amount", + "currency", + "description", + "status", + "failure_reason_code", + "failure_reason", + "meta", + "links" + ] } \ No newline at end of file diff --git a/fixtures/credits.json b/fixtures/credits.json index 800d5b7..c2a3a73 100644 --- a/fixtures/credits.json +++ b/fixtures/credits.json @@ -8,32 +8,27 @@ "credits.reversals": { "type": "string", "format": "uri", - "pattern": "/credits/{credits.id}/reversals", - "required": true + "pattern": "/credits/{credits.id}/reversals" }, "credits.customer": { "type": "string", "format": "uri", - "pattern": "/customers/{credits.customer}", - "required": true + "pattern": "/customers/{credits.customer}" }, "credits.order": { "type": "string", "format": "uri", - "pattern": "/orders/{credits.order}", - "required": true + "pattern": "/orders/{credits.order}" }, "credits.destination": { "type": "string", "format": "uri", - "pattern": "/resources/{credits.destination}", - "required": true + "pattern": "/resources/{credits.destination}" }, "credits.events": { "type": "string", "format": "uri", - "pattern": "/credits/{credits.id}/events", - "required": true + "pattern": "/credits/{credits.id}/events" } }, "required": [ @@ -45,8 +40,7 @@ ] }, "meta": { - "type": "object", - "required": false + "type": "object" }, "credits": { "items": { @@ -54,8 +48,7 @@ }, "type": "array", "minItems": 1, - "uniqueItems": true, - "required": true + "uniqueItems": true } }, "required": [ diff --git a/fixtures/reversals.json b/fixtures/reversals.json new file mode 100644 index 0000000..93f1ae7 --- /dev/null +++ b/fixtures/reversals.json @@ -0,0 +1,48 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "links": { + "type": "object", + "properties": { + "reversals.credit": { + "type": "string", + "format": "uri", + "pattern": "/credits/{reversals.credit}", + "required": true + }, + "reversals.order": { + "type": "string", + "format": "uri", + "pattern": "/orders/{reversals.order}", + "required": true + }, + "reversals.events": { + "type": "string", + "format": "uri", + "pattern": "/reversals/{reversals.id}/events", + "required": true + } + }, + "required": [ + "reversals.credit", + "reversals.order", + "reversals.events" + ] + }, + "meta": { + "type": "object" + }, + "reversals": { + "items": { + "$ref": "_models/reversal.json" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true + } + }, + "required": [ + "reversals" + ] +} \ No newline at end of file From 07fe36d32dea7830190b6726de895ce071246277 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Tue, 17 Dec 2013 18:03:57 -0800 Subject: [PATCH 049/154] reversal features --- features/rest/reversals.feature | 52 +++++++++++++++++++++++++- features/step_definitions/reversals.rb | 6 +++ fixtures/_models/reversal.json | 4 ++ 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 features/step_definitions/reversals.rb diff --git a/features/rest/reversals.feature b/features/rest/reversals.feature index b4fd6ef..342c300 100644 --- a/features/rest/reversals.feature +++ b/features/rest/reversals.feature @@ -2,6 +2,54 @@ Feature: Reversal Reversals allow for canceling a credit to a bank account. A reversals can be partial or the full amount. - @failing Scenario: Creating a full reversal - Given I have credit a bank account + Given I have a bank account with a credit + When I POST to /credits/:credit_id/reversals + Then I should get a 201 Created status code + And the response is valid according to the "reversals" schema + + Scenario: Creating a partial reversal + Given I have a bank account with a credit + When I POST to /credits/:credit_id/reversals with the JSON API body: + """ + { + "amount": 100 + } + """ + Then I should get a 201 Created status code + And the response is valid according to the "reversals" schema + And the fields on this reversal match: + """ + { + "amount": 100 + } + """ + + Scenario: List reversals + Given I have a bank account with a reversal + When I GET to /credits/:credit_id/reversals + Then I should get a 200 OK status code + And the response is valid according to the "reversals" schema + + Scenario: Retrieving a reversal + Given I have a bank account with a reversal + When I GET to /reversals/:reversal_id + Then I should get a 200 OK status code + And the response is valid according to the "reversals" schema + + Scenario: Update a reversal + Given I have a bank account with a reversal + When I PUT to /reversals/:reversal_id with the JSON API body: + """ + { + "description": "merchant did not ship item, taking my money back" + } + """ + Then I should get a 200 OK status code + And the response is valid according to the "reversals" schema + And the fields on this reversal match: + """ + { + "description": "merchant did not ship item, taking my money back" + } + """ diff --git a/features/step_definitions/reversals.rb b/features/step_definitions/reversals.rb new file mode 100644 index 0000000..b5a8685 --- /dev/null +++ b/features/step_definitions/reversals.rb @@ -0,0 +1,6 @@ +Given(/^I have a bank account with a reversal$/) do + step 'I have a bank account with a credit' + @client.post("/credits/#{@credit_id}/reversals", {}) + @reversal_id = @client['reversals']['id'] + @client.add_hydrate :reversal_id, @reversal_id +end diff --git a/fixtures/_models/reversal.json b/fixtures/_models/reversal.json index 0a7de21..f4f59e6 100644 --- a/fixtures/_models/reversal.json +++ b/fixtures/_models/reversal.json @@ -49,6 +49,10 @@ "null" ] }, + "transaction_number": { + "type": "string", + "pattern": "RV[0-9]{3}-[0-9]{3}-[0-9]{4}" + }, "meta": { "type": "object" }, From 8db133e2943158444b5de5859f75eee78c9671be Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Tue, 17 Dec 2013 18:24:10 -0800 Subject: [PATCH 050/154] events schemas --- fixtures/_models/event.json | 97 +++++++++++++++++++++++++++++-------- fixtures/events.json | 24 +++++++++ 2 files changed, 102 insertions(+), 19 deletions(-) create mode 100644 fixtures/events.json diff --git a/fixtures/_models/event.json b/fixtures/_models/event.json index a73aee3..fb958ad 100644 --- a/fixtures/_models/event.json +++ b/fixtures/_models/event.json @@ -2,52 +2,111 @@ "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "properties": { - "href": { + "id": { "type": "string", - "format": "uri", - "required": true + "pattern": "EV[a-zA-Z0-9]{16,32}" }, - "id": { + "href": { "type": "string", - "pattern": "EV[a-zA-Z0-9]{16,32}", - "required": true + "format": "uri" }, "occurred_at": { "type": "string", - "format": "date-time", - "required": true + "format": "date-time" }, "type": { - "type": "string", - "required": true + "type": "string" }, "entity": { "anyOf": [ { - "$ref": "_models/bank_accounts.json" + "bank_accounts": { + "type": "array", + "item": { + "$ref": "_models/bank_accounts.json" + }, + "minItems": 1, + "uniqueItems": true + } }, { - "$ref": "_models/bank_account_verifications.json" + "bank_account_verifications": { + "type": "array", + "item": { + "$ref": "_models/bank_account_verifications.json" + }, + "minItems": 1, + "uniqueItems": true + } }, { - "$ref": "_models/card_holds.json" + "card_holds": { + "type": "array", + "item": { + "$ref": "_models/card_holds.json" + }, + "minItems": 1, + "uniqueItems": true + } }, { - "$ref": "_models/credits.json" + "credits": { + "type": "array", + "item": { + "$ref": "_models/credits.json" + }, + "minItems": 1, + "uniqueItems": true + } }, { - "$ref": "_models/customers.json" + "customers": { + "type": "array", + "item": { + "$ref": "_models/customers.json" + }, + "minItems": 1, + "uniqueItems": true + } }, { - "$ref": "_models/debits.josn" + "debits": { + "type": "array", + "item": { + "$ref": "_models/debits.josn" + }, + "minItems": 1, + "uniqueItems": true + } }, { - "$ref": "_models/refunds.json" + "debits": { + "type": "array", + "item": { + "$ref": "_models/refunds.json" + }, + "minItems": 1, + "uniqueItems": true + } }, { - "$ref": "_models/reversals.json" + "reversals": { + "type": "array", + "item": { + "$ref": "_models/reversals.json" + }, + "minItems": 1, + "uniqueItems": true + } } ] } - } + }, + "required": [ + "id", + "href", + "occurred_at", + "type", + "entity" + ] } \ No newline at end of file diff --git a/fixtures/events.json b/fixtures/events.json new file mode 100644 index 0000000..7baa290 --- /dev/null +++ b/fixtures/events.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "links": { + "type": "object", + "properties": {} + }, + "meta": { + "type": "object" + }, + "events": { + "items": { + "$ref": "_models/event.json" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true + } + }, + "required": [ + "events" + ] +} \ No newline at end of file From 12689e8472775a607b2dc9d054a147ce5ceb59b4 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Tue, 17 Dec 2013 18:34:22 -0800 Subject: [PATCH 051/154] events features --- features/rest/events.feature | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 features/rest/events.feature diff --git a/features/rest/events.feature b/features/rest/events.feature new file mode 100644 index 0000000..7a671bd --- /dev/null +++ b/features/rest/events.feature @@ -0,0 +1,9 @@ +Feature: Events + Events represent action that occue inside the Balanced system. + They can not be created by the customer, and instead are automatically created + when actions are completed. + + Scenario: List events + When I GET to /events + Then I should get a 200 OK status code + And the response is valid according to the "events" schema From f06b356a20e23a378df7bc33c4597dc4473be52c Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Thu, 19 Dec 2013 22:53:15 +0000 Subject: [PATCH 052/154] Add an alias for requests using different language The phrase "I POST to /foo" and "Make a GET request to /foo" both sound 'correct.' So let's support both, depending on whichever sounds better. --- features/step_definitions/http_steps.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 4a3f1e1..c011872 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -2,6 +2,10 @@ @client.verb(verb, @client.hydrater(url)) end +When(/^I make a (\w+) request to (\/\S*?)$/) do |verb, url| + step "I #{verb} to #{url}" +end + When(/^I POST to (\/.*) without my secret key with the JSON API body:$/) do |url, body| # use for tokenizing cards and bank accounts options = { From bce337967710d3e53b7198da3bf9046b235aa89d Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Thu, 19 Dec 2013 22:54:55 +0000 Subject: [PATCH 053/154] New feature: root resource --- features/root.feature | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 features/root.feature diff --git a/features/root.feature b/features/root.feature new file mode 100644 index 0000000..0c270b4 --- /dev/null +++ b/features/root.feature @@ -0,0 +1,12 @@ +Feature: Root resource + The root resource is kind of funny, because while it's one of the simplest, + it's also one of the most important. + + This is where all hypermedia style interaction begins from. Right now it's + not that way, but it will be at some point. + + Scenario: Request the root resource + The primary interaction with the root is to fetch it. + + When I make a GET request to / + Then I should get a 200 status code From f21ca0d25b85b3f86b4f5f3cae0da73c576589ea Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Thu, 19 Dec 2013 23:24:57 +0000 Subject: [PATCH 054/154] New feature: the resources resource --- features/resources.feature | 16 ++++++++++++++++ features/step_definitions/customers.rb | 4 ++++ 2 files changed, 20 insertions(+) create mode 100644 features/resources.feature diff --git a/features/resources.feature b/features/resources.feature new file mode 100644 index 0000000..147e602 --- /dev/null +++ b/features/resources.feature @@ -0,0 +1,16 @@ +Feature: The resources resource. + The resources resource is interesting. Basically, it's a global lookup + system. It allows you to take the unique ID of any Balanced entity and + get a representation. + + Such meta. + + Scenario: Look up a resource. + As an example, maybe a Balanced user has stored a Customer ID rather than a + Customer URL. This allows them to retrieve a Customer representation without + worrying about the actual structure of our usual URLs. + + Given I have created a customer + When I GET to /resources/:customer_id giving the customer_id + Then I should get a 200 OK status code + And the response is valid according to the "customers" schema diff --git a/features/step_definitions/customers.rb b/features/step_definitions/customers.rb index 781841d..2f05453 100644 --- a/features/step_definitions/customers.rb +++ b/features/step_definitions/customers.rb @@ -12,6 +12,10 @@ @client.get("/customers/#{@customer_id}") end +When(/^I GET to \/resources\/:customer_id giving the customer_id$/) do + @client.get("/resources/#{@customer_id}") +end + When(/^I DELETE to \/customers\/:customer_id giving the customer_id$/) do options = { headers: { From cf4036bf7a6c21f3afd428551ca977705a3a2c57 Mon Sep 17 00:00:00 2001 From: Dave Clausen Date: Thu, 19 Dec 2013 18:45:22 -0800 Subject: [PATCH 055/154] minor spelling updates --- features/rest/bank_accounts.feature | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/rest/bank_accounts.feature b/features/rest/bank_accounts.feature index eac7ad0..847907c 100644 --- a/features/rest/bank_accounts.feature +++ b/features/rest/bank_accounts.feature @@ -1,6 +1,6 @@ Feature: Bank accounts - The bank account resource respresent a funding source or - destiation that is backed by a bank account. Marketplaces + The bank account resource respresents a funding source or + destination that is backed by a bank account. Marketplaces are able to credit out to bank accounts without doing any verifications but to debit from a bank account, micro deposit verifications are required. From d5c1a3ed7fbc09b4f789a39c78a6146e010f904c Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 20 Dec 2013 22:31:16 +0000 Subject: [PATCH 056/154] Better error messages on status code failure. Basically, "Got a 409, expected a 201" isn't actually very helpful. We can grab the error description out of the body and print it out, and it's way more helpful. --- features/step_definitions/http_steps.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index c011872..45cf993 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -53,7 +53,8 @@ end Then(/^I should get a (.+) status code$/) do |code| - assert_equal code.to_i, @client.last_code + message = @client.last_body["errors"][0]["description"] rescue "" + assert_equal code.to_i, @client.last_code, message end Then(/^there should be no response body$/) do From 8e820bfdc4dfe49c65e3fb8c791adb38b3744b8b Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 20 Dec 2013 23:16:41 +0000 Subject: [PATCH 057/154] New feature: create a reversal. This is probably the worst commit I've made in a while. I'm basically not happy with any real part of it. But it works. We use the url_template gem to handle url templating, but it isn't expanding variable names correctly, so I'm doing that myself. With gsub. Hopefully I can clean that up. Furthermore, we need to sort of store an 'environment' of mappings of ivars to the template names. Global-ish mutable state makes me very uneasy... @matthewfl, I want your opinion on how to not make this suck. --- Gemfile | 1 + Gemfile.lock | 2 ++ features/reversals.feature | 16 ++++++++++++++++ features/step_definitions/http_steps.rb | 24 ++++++++++++++++++++++++ features/support/initial_setup.rb | 11 +++++++++++ lib/balanced/tiny_client.rb | 15 ++++++++++++--- 6 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 features/reversals.feature diff --git a/Gemfile b/Gemfile index 983fc78..162433d 100644 --- a/Gemfile +++ b/Gemfile @@ -3,4 +3,5 @@ source "https://rubygems.org" gem "cucumber" gem "httparty" gem "json-schema" +gem "uri_template" gem "rake" diff --git a/Gemfile.lock b/Gemfile.lock index 4f77649..c073ad4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -20,6 +20,7 @@ GEM multi_test (0.0.2) multi_xml (0.5.5) rake (10.1.0) + uri_template (0.6.0) PLATFORMS ruby @@ -29,3 +30,4 @@ DEPENDENCIES httparty json-schema rake + uri_template diff --git a/features/reversals.feature b/features/reversals.feature new file mode 100644 index 0000000..2f76efe --- /dev/null +++ b/features/reversals.feature @@ -0,0 +1,16 @@ +Feature: Reversals + + Scenario: Reverse a credit. + + Given I have tokenized a bank account + When I make a POST request to the link "bank_accounts.credits" with the body: + """ + { "amount": 2000 } + """ + Then I should get a 201 Created status code + And the response is valid according to the "credits" schema + + When I make a POST request to the link "credits.reversals" + Then I should get a 201 Created status code + And the response is valid according to the "reversals" schema + diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 45cf993..dcbb0ba 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -6,6 +6,30 @@ step "I #{verb} to #{url}" end +def env + { + "bank_accounts_id" => @bank_account_id, + "credits_id" => @credit_id, + } +end + + +When(/^I make a POST request to the href "(.*?)"$/) do |keys| + @client.post(@client.inject(keys), {}, env) +end + +When(/^I make a POST request to the link "(.*?)" with the body:$/) do |keys, body| + body = @client.post(@client.hydrater(@client.last_body["links"][keys]), JSON.parse(body), env) + @credit_id = @client['credits']['id'] rescue nil + body +end + +When(/^I make a POST request to the link "(.*?)"$/) do |keys| + body = @client.post(@client.hydrater(@client.last_body["links"][keys]), {}, env) + @credit_id = @client['credits']['id'] rescue nil + body +end + When(/^I POST to (\/.*) without my secret key with the JSON API body:$/) do |url, body| # use for tokenizing cards and bank accounts options = { diff --git a/features/support/initial_setup.rb b/features/support/initial_setup.rb index fae20c7..b02035a 100644 --- a/features/support/initial_setup.rb +++ b/features/support/initial_setup.rb @@ -30,6 +30,17 @@ HTTParty.post("#{$root_url}/marketplaces", options) +# create an initial amount of money in the marketplace +# Sorry, whoever owns this card. ;) +HTTParty.post("#{$root_url}/debits", options.merge(body: { + amount: 10_000_000, # WE"RE RICH!!!! + source: { + number: "4111111111111111", + expiration_year: "2018", + expiration_month: 12 + } +})) + $:.unshift(File.dirname(__FILE__)+'/../../lib') require 'balanced/tiny_client' diff --git a/lib/balanced/tiny_client.rb b/lib/balanced/tiny_client.rb index adaeffc..012974e 100644 --- a/lib/balanced/tiny_client.rb +++ b/lib/balanced/tiny_client.rb @@ -15,7 +15,7 @@ def initialize(api_secret, accept_header, root_url) @hydrate_tokens = {} end - def post(endpoint, body) + def post(endpoint, body, env={}) body = JSON.parse(body) if body.is_a? String options = { headers: { @@ -28,7 +28,16 @@ def post(endpoint, body) } } - response = HTTParty.post("#{@root_url}#{endpoint}", options) + require 'uri_template' + # I am so sorry + template = "#{@root_url}#{endpoint}".gsub(/{(.*?)}/) do + "{#{$1.gsub(".", "_")}}" + end + # TODO: does using . in uri variables make sense? http://tools.ietf.org/html/rfc6570#section-3.2.1 + template = URITemplate.new(template) + url = template.expand(env) + + response = HTTParty.post(url, options) @responses << response response end @@ -63,7 +72,7 @@ def add_response(response) @responses << response end - def verb(verb, url) + def verb(verb, url, env={}) options = { headers: { 'Accept' => @accept_header From 967a193026947f3577794ce585c8f4e9d9a383b4 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 20 Dec 2013 23:44:47 +0000 Subject: [PATCH 058/154] New feature: failed reversal. Issue #455 is tracking the failure. --- features/reversals.feature | 19 +++ features/step_definitions/reversals.rb | 4 +- fixtures/errors.json | 189 +++++++++++++++++++++++++ 3 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 fixtures/errors.json diff --git a/features/reversals.feature b/features/reversals.feature index 2f76efe..7c984e6 100644 --- a/features/reversals.feature +++ b/features/reversals.feature @@ -14,3 +14,22 @@ Feature: Reversals Then I should get a 201 Created status code And the response is valid according to the "reversals" schema + @failing + Scenario: Failed reversal + You can't reverse a particular amount. + + Given I have tokenized a bank account + When I make a POST request to the link "bank_accounts.credits" with the body: + """ + { "amount": 2000 } + """ + Then I should get a 201 Created status code + And the response is valid according to the "credits" schema + + When I make a POST request to the link "credits.reversals" with the body: + """ + { "amount": 10000000 } + """ + Then I should get a 400 status code + And the response is valid according to the "errors" schema + diff --git a/features/step_definitions/reversals.rb b/features/step_definitions/reversals.rb index b5a8685..0e616ab 100644 --- a/features/step_definitions/reversals.rb +++ b/features/step_definitions/reversals.rb @@ -1,6 +1,8 @@ Given(/^I have a bank account with a reversal$/) do step 'I have a bank account with a credit' + @client.post("/credits/#{@credit_id}/reversals", {}) - @reversal_id = @client['reversals']['id'] + @reversal_id = @client['reversals']['id'] rescue nil + @client.add_hydrate :reversal_id, @reversal_id end diff --git a/fixtures/errors.json b/fixtures/errors.json new file mode 100644 index 0000000..9b8b964 --- /dev/null +++ b/fixtures/errors.json @@ -0,0 +1,189 @@ +{ + "type": "object", + "properties": { + "status": { + "type": "string", + "required": true + }, + "category_code": { + "type": "string", + "enum": [ + "account-insufficient-funds", + "authorization-failed", + "card-declined", + "funding-destination-declined", + "account-already-merchant", + "address-verification-failed", + "authorization-expired", + "bank-account-already-associated", + "bank-account-authentication-already-exists", + "bank-account-authentication-failed", + "bank-account-authentication-not-pending", + "bank-account-not-associated", + "bank-account-not-valid", + "business-kyc", + "business-principal-kyc", + "cannot-associate-bank-account", + "cannot-associate-card", + "cannot-associate-merchant-with-account", + "cannot-capture-authorization", + "cannot-void-authorization", + "capture-void-attempt", + "card-already-funding-src", + "card-not-associated", + "card-not-valid", + "card-not-validated", + "credit-already-reversed", + "debit-already-refunded", + "debit-not-found", + "duplicate-email-address", + "funding-destination-already-associated", + "funding-destination-cannot-reverse", + "funding-destination-not-associated", + "funding-destination-not-creditable", + "funding-source-already-associated", + "funding-source-not-associated", + "funding-source-not-authorizable", + "funding-source-not-debitable", + "funding-source-not-hold", + "funding-source-not-refundable", + "hold-not-associated-account", + "hold-not-associated-marketplace", + "identity-verification-error", + "insufficient-funds", + "marketplace-already-created", + "marketplace-cannot-prepay-fees", + "multiple-debits", + "no-funding-destination", + "no-funding-source", + "person-kyc", + "precog-logical-error", + "refund-insufficient-funds", + "reverse-void-attempt", + "unexpected-payload", + "bank-account-authentication-forbidden", + "cannot-reverse-credit", + "incomplete-account-info", + "invalid-amount", + "invalid-bank-account-number", + "invalid-reversal-amount", + "invalid-routing-number", + "precog-bad-request", + "refund-invalid-debit-state", + "not-found", + "patch-test-failed", + "request", + "order-kyc", + "method-not-allowed" + ], + "required": true + }, + "description": { + "__example": "Human readable error message about what went wrong. Your request id is OHMasdfasdfasdfasdf", + "anyOf": [ + { + "type": "string", + "enum": [ + "Account balance is insufficient to issue this debit", + "An account with the given email address already exists.", + "Bank account authentication amounts do not match.", + "Bank account authentication amounts do not match.", + "Bank account authentication is not allowed.", + "Bank account authentication is not in state ``pending``.", + "Credit has already been fully reversed.", + "Credit to reversal must have ``succeeded``.", + "Debit has already been fully refunded.", + "Marketplace balance is insufficient to issue this credit", + "Marketplace escrow balance is insufficient to issue this credit.", + "Marketplace escrow balance is insufficient to issue this refund.", + "Marketplace has already been created.", + "No buyer or merchant info was provided. Either \"merchant/merchant_uri\" or\n\"card/card_uri\" fields must be present.", + "Refund amount cannot be greater than the amount of the original debit.", + "Reverse amount cannot be greater than the amount of the original credit.", + "State of debit to refund must be ``succeeded``.", + "The account has no valid funding destinations.", + "The account has no valid funding sources.", + "The account is already a merchant.", + "The bank account is already associated with an account -- it cannot be\nassociated.", + "The bank account is not associated with the given account.", + "The bank account was tokenized on a different marketplace than the one\nused for this request.", + "The card could not be validated -- either the card number or security code\nmay be wrong.", + "The card is already associated with an account -- it cannot be associated\nagain.", + "The card is not associated with the given account.", + "The card was tokenized on a different marketplace than the one used for\nthis request.", + "The given funding destination cannot have a credit created against it.", + "The given funding destination cannot reverse.", + "The given funding destination is already associated with an account.", + "The given funding destination is not associated with the account.", + "The given funding source cannot be refunded.", + "The given funding source cannot have a debit created against it.", + "The given funding source cannot have a hold created against it.", + "The given funding source cannot have an authorization created against\nit. Authorizations are only valid for card-type funding sources.", + "The given funding source is already associated with an account.", + "The given funding source is not associated with an account.", + "The merchant information was created on a different marketplace than the\none used for this request.", + "The processor did not accept the transaction.", + "The processor did not accept this hold.", + "The routing number provided for the bank account was invalid.", + "This address could not be verified.", + "This bank account has already been marked as invalid/deactivated. It cannot\nbe used again.", + "This bank account number is not alpha numeric (ascii). It can not be used.", + "This card has already been marked as invalid/deactivated. It cannot be used\nagain.", + "This card was declined by the processor.", + "This hold has already been captured or voided, and cannot be captured.", + "This hold has already been captured or voided, and cannot be voided.", + "This hold has already been captured, which cannot be reversed.", + "This hold has already been voided, which cannot be reversed.", + "This hold has already expired.", + "This hold is not associated with this account.", + "This hold is not associated with this marketplace.", + "Order escrow balanced is insufficient to issue this credit.", + "Order escrow balanced is insufficient to issue this refund.", + "Order requires merchant be underwritten." + ] + }, + { + "type": "string", + "pattern": ".* Your request id is OHM[a-zA-Z0-9]*\\." + }, + { + "type": "null" + } + ], + "required": true + }, + "additional": { + "required": false + }, + "extras": { + "required": false + }, + "status_code": { + "type": "integer", + "enum": [ + 400, + 402, + 403, + 405, + 404, + 409 + ], + "required": true + }, + "category_type": { + "type": "string", + "enum": [ + "request", + "banking", + "logical" + ], + "required": true + }, + "request_id": { + "type": "string", + "pattern": "OHM[a-zA-Z0-9]{16,34}", + "required": true + } + }, + "additionalProperties": false +} From b89b6e37b98d30d669e9baa78a4c9a70e71102ea Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Sat, 21 Dec 2013 01:22:01 +0000 Subject: [PATCH 059/154] New feature: failed reversal on bank account. Issue #456 is tracking the failure. --- features/reversals.feature | 21 +++++++++++++++++++++ features/step_definitions/http_steps.rb | 4 +++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/features/reversals.feature b/features/reversals.feature index 7c984e6..ce211b7 100644 --- a/features/reversals.feature +++ b/features/reversals.feature @@ -33,3 +33,24 @@ Feature: Reversals Then I should get a 400 status code And the response is valid according to the "errors" schema + @failing + Scenario: Reverse a bank account credit + + When I POST to /credits with the JSON API body: + """ + { + "amount": 1234, + "destination": { + "name": "Michael Jordan", + "account_number": "9900000004", + "routing_number": "021000021", + "account_type": "checking" + } + } + """ + Then I should get a 201 Created status code + And the response is valid according to the "credits" schema + + When I make a POST request to the link "credits.reversals" + Then I should get a 201 Created status code + And the response is valid according to the "reversals" schema diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index dcbb0ba..8e2f74d 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -60,7 +60,9 @@ def env end When(/^I POST to (\/\S*) with the JSON API body:$/) do |url, body| - @client.post(@client.hydrater(url), body) + body = @client.post(@client.hydrater(url), body) + @credit_id = @client['credits']['id'] rescue nil + body end When(/^I PUT to (\/\S*) with the JSON API body:$/) do |url, body| From ed9340d809623d6933a75da500e2ae2c0148dd57 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Sat, 21 Dec 2013 01:28:28 +0000 Subject: [PATCH 060/154] New feature: successful reversal --- features/reversals.feature | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/features/reversals.feature b/features/reversals.feature index ce211b7..bb7d390 100644 --- a/features/reversals.feature +++ b/features/reversals.feature @@ -54,3 +54,24 @@ Feature: Reversals When I make a POST request to the link "credits.reversals" Then I should get a 201 Created status code And the response is valid according to the "reversals" schema + + Scenario: Reverse a bank account credit + + When I POST to /credits with the JSON API body: + """ + { + "amount": 1234, + "destination": { + "name": "Michael Jordan", + "account_number": "9900000095", + "routing_number": "021000021", + "account_type": "checking" + } + } + """ + Then I should get a 201 Created status code + And the response is valid according to the "credits" schema + + When I make a POST request to the link "credits.reversals" + Then I should get a 201 Created status code + And the response is valid according to the "reversals" schema From b2a5c7c791bda9849b7c0f7ad9f229ce209a7f56 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Sat, 21 Dec 2013 01:38:35 +0000 Subject: [PATCH 061/154] New feature: debit a card --- features/debits.feature | 11 +++++++++++ features/step_definitions/http_steps.rb | 2 ++ 2 files changed, 13 insertions(+) create mode 100644 features/debits.feature diff --git a/features/debits.feature b/features/debits.feature new file mode 100644 index 0000000..22ff610 --- /dev/null +++ b/features/debits.feature @@ -0,0 +1,11 @@ +Feature: Debit cards + + Scenario: Debit a card + Given I have tokenized a card + When I make a POST request to the link "cards.debits" with the body: + """ + { "amount": 2000 } + """ + Then I should get a 201 Created status code + And the response is valid according to the "debits" schema + diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 8e2f74d..d2f748e 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -10,6 +10,7 @@ def env { "bank_accounts_id" => @bank_account_id, "credits_id" => @credit_id, + "cards_id" => @card_id, } end @@ -21,6 +22,7 @@ def env When(/^I make a POST request to the link "(.*?)" with the body:$/) do |keys, body| body = @client.post(@client.hydrater(@client.last_body["links"][keys]), JSON.parse(body), env) @credit_id = @client['credits']['id'] rescue nil + @cards_id = @client['cards']['id'] rescue nil body end From 832513786f6c91e8933b38888c8800949a6ab627 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Sat, 21 Dec 2013 01:43:58 +0000 Subject: [PATCH 062/154] make a distinction between having a card and a customer card. We were doing it the wrong way before. --- features/step_definitions/cards.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/features/step_definitions/cards.rb b/features/step_definitions/cards.rb index a70b4ca..71f2a89 100644 --- a/features/step_definitions/cards.rb +++ b/features/step_definitions/cards.rb @@ -1,4 +1,21 @@ Given(/^I have tokenized a card$/) do + @client.post('/cards', + { + number: "4111 1111 1111 1111", + expiration_month: 12, + expiration_year: 2016, + cvv: "123", + address: { + line1: "965 Mission St", + postal_code: "94103" + } + } + ) + @card_id = @client['cards']['id'] + @client.add_hydrate(:card_id, @card_id) +end + +Given(/^I have tokenized a customer card$/) do @client.post('/cards', { number: "4111111111111111", From 1627bab78d60297a717adf082792129ed1ce139a Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Sat, 21 Dec 2013 01:44:36 +0000 Subject: [PATCH 063/154] New feature: debit a customer card --- features/debits.feature | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/features/debits.feature b/features/debits.feature index 22ff610..688ce57 100644 --- a/features/debits.feature +++ b/features/debits.feature @@ -9,3 +9,11 @@ Feature: Debit cards Then I should get a 201 Created status code And the response is valid according to the "debits" schema + Scenario: Debit a card + Given I have tokenized a customer card + When I make a POST request to the link "cards.debits" with the body: + """ + { "amount": 2000 } + """ + Then I should get a 201 Created status code + And the response is valid according to the "debits" schema From 602214e9cfe4fcf4b7dd8e19598090a73dada4ef Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Sat, 21 Dec 2013 23:00:24 +0000 Subject: [PATCH 064/154] New feature: create a customer --- features/customers.feature | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 features/customers.feature diff --git a/features/customers.feature b/features/customers.feature new file mode 100644 index 0000000..b29e819 --- /dev/null +++ b/features/customers.feature @@ -0,0 +1,17 @@ +Feature: Customers + + Scenario: Creating a customer + When I POST to /customers with the JSON API body: + """ + { + "name": "Balanced Testing" + } + """ + Then I should get a 201 Created status code + And the response is valid according to the "customers" schema + And the fields on this customer match: + """ + { + "name": "Balanced Testing" + } + """ From ef2eecb0b50085d409cae9046812286ff94fd57c Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Wed, 25 Dec 2013 22:20:01 +0000 Subject: [PATCH 065/154] fix description around customer cards --- features/debits.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/debits.feature b/features/debits.feature index 688ce57..370b1e7 100644 --- a/features/debits.feature +++ b/features/debits.feature @@ -9,7 +9,7 @@ Feature: Debit cards Then I should get a 201 Created status code And the response is valid according to the "debits" schema - Scenario: Debit a card + Scenario: Debit a customer card Given I have tokenized a customer card When I make a POST request to the link "cards.debits" with the body: """ From 71d9ae003de3c9882bb0d130096f59c6c8f20f25 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Wed, 25 Dec 2013 22:38:34 +0000 Subject: [PATCH 066/154] new feature: refunds create --- features/refunds.feature | 9 +++++++++ features/step_definitions/cards.rb | 12 ++++++++++++ features/step_definitions/http_steps.rb | 1 + 3 files changed, 22 insertions(+) create mode 100644 features/refunds.feature diff --git a/features/refunds.feature b/features/refunds.feature new file mode 100644 index 0000000..9e77ad9 --- /dev/null +++ b/features/refunds.feature @@ -0,0 +1,9 @@ +Feature: Refunds + + Scenario: Create a refund + Given I have tokenized a customer card + And I have debited that card + + When I make a POST request to the link "debits.refunds" + Then I should get a 201 Created status code + And the response is valid according to the "refunds" schema diff --git a/features/step_definitions/cards.rb b/features/step_definitions/cards.rb index 71f2a89..72aa936 100644 --- a/features/step_definitions/cards.rb +++ b/features/step_definitions/cards.rb @@ -27,6 +27,18 @@ @client.add_hydrate(:card_id, @card_id) end +Given(/^I have debited that card$/) do + @client.post(@client.hydrater(@client.last_body["links"]["cards.debits"]), + { + amount: 2000, + }, + env + ) + @debit_id = @client['debits']['id'] + @client.add_hydrate(:debit_id, @debit_id) +end + + Given(/^I have tokenized more than one card$/) do 2.times { step "I have tokenized a card" } end diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index d2f748e..777025b 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -11,6 +11,7 @@ def env "bank_accounts_id" => @bank_account_id, "credits_id" => @credit_id, "cards_id" => @card_id, + "debits_id" => @debit_id, } end From 2a70aac6bf8f89a200a46dd0e7f8a9e9e2ca9538 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 27 Dec 2013 23:05:35 +0000 Subject: [PATCH 067/154] Add HTTP PATCH to tiny client. We need this for increasingly more scenarios --- lib/balanced/tiny_client.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/balanced/tiny_client.rb b/lib/balanced/tiny_client.rb index 012974e..fe366fb 100644 --- a/lib/balanced/tiny_client.rb +++ b/lib/balanced/tiny_client.rb @@ -60,6 +60,25 @@ def put(endpoint, body) response end + def patch(endpoint, body) + body = JSON.parse(body) if body.is_a? String + options = { + headers: { + 'Accept' => @accept_header, + 'Content-Type' => "application/json", # github: https://github.com/balanced/balanced-api/issues/458 + }, + body: JSON.dump(body), # sometimes we send arrays and then it gets confused + basic_auth: { + username: @api_secret, + password: '', + } + } + + response = HTTParty.patch("#{@root_url}#{endpoint}", options) + @responses << response + response + end + def get(endpoint) verb 'GET', endpoint end From 3d1c8bf7c5d589cef648a2d70fd11d8677d2bee3 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 27 Dec 2013 23:05:59 +0000 Subject: [PATCH 068/154] Log secret and marketplace ID This is needed for debugging when things go sideways. --- features/support/initial_setup.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/features/support/initial_setup.rb b/features/support/initial_setup.rb index b02035a..16184c5 100644 --- a/features/support/initial_setup.rb +++ b/features/support/initial_setup.rb @@ -14,6 +14,7 @@ } response = HTTParty.post("#{$root_url}/api_keys", options) $api_secret = JSON.parse(response.body)["api_keys"][0]["secret"] +puts "SECRET: #{$api_secret}" # Now that we have our key, we need to make a marketplace. Lots of our scenarios # will fail unless we've made at least one. @@ -28,7 +29,9 @@ }, } -HTTParty.post("#{$root_url}/marketplaces", options) +response = HTTParty.post("#{$root_url}/marketplaces", options) +marketplace_id = response["marketplaces"][0]["id"] +puts "MARKETPLACE: #{marketplace_id}" # create an initial amount of money in the marketplace # Sorry, whoever owns this card. ;) From 27ed2d196fe999c1dd6ae7be18cffad166526365 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 27 Dec 2013 23:06:12 +0000 Subject: [PATCH 069/154] New feature: create a debit --- features/debits.feature | 9 ++++++ features/step_definitions/customers.rb | 40 +++++++++++++++++++++++++ features/step_definitions/http_steps.rb | 1 + 3 files changed, 50 insertions(+) diff --git a/features/debits.feature b/features/debits.feature index 370b1e7..26016e6 100644 --- a/features/debits.feature +++ b/features/debits.feature @@ -17,3 +17,12 @@ Feature: Debit cards """ Then I should get a 201 Created status code And the response is valid according to the "debits" schema + + Scenario: Create a debit + Given I have created a customer + When I make a POST request to the link "customers.debits" with the body: + """ + { "amount": 1234 } + """ + Then I should get a 201 Created status code + And the response is valid according to the "debits" schema diff --git a/features/step_definitions/customers.rb b/features/step_definitions/customers.rb index 2f05453..e2d20cc 100644 --- a/features/step_definitions/customers.rb +++ b/features/step_definitions/customers.rb @@ -2,8 +2,48 @@ @client.post('/customers', {}) @customer_id = @client['id'] @client.add_hydrate :customer_id, @customer_id + + customer_url = @client['customers']['href'] + + # tokenize a card for them + @client.post('/cards', + { + number: "4111 1111 1111 1111", + expiration_month: 12, + expiration_year: 2016, + cvv: "123", + address: { + line1: "965 Mission St", + postal_code: "94103" + } + } + ) + card_url = @client['cards']['href'] + card_id = @client['cards']['id'] + @client.patch(card_url, + [{ + op: "replace", + path: "/cards/0/links/customer", + value: @customer_id, + }] + ) + + # associate their card so that they have a funding source + @client.patch(customer_url, + [{ + op: "replace", + path: "/customers/0/links/source", + value: card_id + }] + ) + + ## TODO: fix hax + # Right now, we rely on last_body in places becuase the client is mutable + # this is bad and we should stop it. + @client.get(customer_url) end + Given(/^I have created more than one customer$/) do 2.times { step "I have created a customer" } end diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 777025b..bcb1c03 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -12,6 +12,7 @@ def env "credits_id" => @credit_id, "cards_id" => @card_id, "debits_id" => @debit_id, + "customers_id" => @customer_id, } end From a9d699e4a0710922cdcdfc624033a632852f7a93 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Sat, 28 Dec 2013 00:02:41 +0000 Subject: [PATCH 070/154] New feature: refunds go to original funding source. I made the last scenario mis-understanding the way that the YAML scenarios worked. Often, they have duplicate stuff with new names just to make sure that things work. That means I don't need to translate them 1-1, I have to figure out which thing is actually important and just do that. --- features/debits.feature | 9 ------- features/refunds.feature | 6 +++++ features/step_definitions/customers.rb | 12 +++++----- features/step_definitions/debits.rb | 7 ++++++ features/step_definitions/http_steps.rb | 1 + features/step_definitions/refund.rb | 32 +++++++++++++++++++++++++ 6 files changed, 52 insertions(+), 15 deletions(-) diff --git a/features/debits.feature b/features/debits.feature index 26016e6..370b1e7 100644 --- a/features/debits.feature +++ b/features/debits.feature @@ -17,12 +17,3 @@ Feature: Debit cards """ Then I should get a 201 Created status code And the response is valid according to the "debits" schema - - Scenario: Create a debit - Given I have created a customer - When I make a POST request to the link "customers.debits" with the body: - """ - { "amount": 1234 } - """ - Then I should get a 201 Created status code - And the response is valid according to the "debits" schema diff --git a/features/refunds.feature b/features/refunds.feature index 9e77ad9..b95b6ba 100644 --- a/features/refunds.feature +++ b/features/refunds.feature @@ -7,3 +7,9 @@ Feature: Refunds When I make a POST request to the link "debits.refunds" Then I should get a 201 Created status code And the response is valid according to the "refunds" schema + + Scenario: Refund after changed source + Given I have created a debit + When I change the default funding source + And I create a refund + Then the refund should go to the original card diff --git a/features/step_definitions/customers.rb b/features/step_definitions/customers.rb index e2d20cc..e46bf67 100644 --- a/features/step_definitions/customers.rb +++ b/features/step_definitions/customers.rb @@ -3,7 +3,7 @@ @customer_id = @client['id'] @client.add_hydrate :customer_id, @customer_id - customer_url = @client['customers']['href'] + @customer_url = @client['customers']['href'] # tokenize a card for them @client.post('/cards', @@ -19,8 +19,8 @@ } ) card_url = @client['cards']['href'] - card_id = @client['cards']['id'] - @client.patch(card_url, + @card_id = @client['cards']['id'] + @client.patch(card_url, [{ op: "replace", path: "/cards/0/links/customer", @@ -29,18 +29,18 @@ ) # associate their card so that they have a funding source - @client.patch(customer_url, + @client.patch(@customer_url, [{ op: "replace", path: "/customers/0/links/source", - value: card_id + value: @card_id }] ) ## TODO: fix hax # Right now, we rely on last_body in places becuase the client is mutable # this is bad and we should stop it. - @client.get(customer_url) + @client.get(@customer_url) end diff --git a/features/step_definitions/debits.rb b/features/step_definitions/debits.rb index 706f8cb..7ea80f8 100644 --- a/features/step_definitions/debits.rb +++ b/features/step_definitions/debits.rb @@ -21,3 +21,10 @@ Given(/^I have more than one debit$/) do 2.times { step 'I have debited a card' } end + +Given(/^I have created a debit$/) do + step 'I have created a customer' + step 'I make a POST request to the link "customers.debits" with the body:', '{ "amount": 1234 }' + step 'I should get a 201 Created status code' + step 'the response is valid according to the "debits" schema' +end diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index bcb1c03..2504de2 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -25,6 +25,7 @@ def env body = @client.post(@client.hydrater(@client.last_body["links"][keys]), JSON.parse(body), env) @credit_id = @client['credits']['id'] rescue nil @cards_id = @client['cards']['id'] rescue nil + @debit_url = @client['debits']['href'] rescue nil body end diff --git a/features/step_definitions/refund.rb b/features/step_definitions/refund.rb index ff11d79..f59325a 100644 --- a/features/step_definitions/refund.rb +++ b/features/step_definitions/refund.rb @@ -4,3 +4,35 @@ @refund_id = @client['refunds']['id'] @client.add_hydrate :refund_id, @refund_id end + +When(/^I change the default funding source$/) do + @client.post('/cards', + { + number: "4111 1111 1111 1111", + expiration_month: 12, + expiration_year: 2016, + } + ) + @other_card_id = @client['cards']['id'] + + @client.patch(@customer_url, + [{ + op: "replace", + path: "/customers/0/links/source", + value: @other_card_id + }] + ) +end + +When(/^I create a refund$/) do + @original_card_id = @card_id # creating the refund will make a new card + step 'I have created a refund for a debit' +end + +Then(/^the refund should go to the original card$/) do + @client.get(@debit_url) + step "I should get a 200 OK status code" + step 'the response is valid according to the "debits" schema' + step "the fields on these debits match:", %Q/{ "links": { "source": "#{@original_card_id}" } }/ +end + From 44512a5dff571410fb09df03a46df0f60c79f219 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Sat, 28 Dec 2013 00:40:25 +0000 Subject: [PATCH 071/154] New feature: create a failing refund This is currently not working. #459 is tracking it. --- features/refunds.feature | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/features/refunds.feature b/features/refunds.feature index b95b6ba..0c42036 100644 --- a/features/refunds.feature +++ b/features/refunds.feature @@ -8,6 +8,20 @@ Feature: Refunds Then I should get a 201 Created status code And the response is valid according to the "refunds" schema + @failing @gh-459 + Scenario: Create a failed refund + Given I have tokenized a customer card + And I have debited that card + + When I make a POST request to the link "debits.refunds" with the body: + """ + { + "amount": 200000000 + } + """ + Then I should get a 400 status code + And the response is valid according to the "errors" schema + Scenario: Refund after changed source Given I have created a debit When I change the default funding source From e86653505bbaf20aed808d58a1205d13ae3f8d74 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Sat, 28 Dec 2013 00:57:41 +0000 Subject: [PATCH 072/154] New feature: debit a verified bank account --- features/debits.feature | 13 +++++++++++++ features/step_definitions/bank_accounts.rb | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/features/debits.feature b/features/debits.feature index 370b1e7..dbb984c 100644 --- a/features/debits.feature +++ b/features/debits.feature @@ -17,3 +17,16 @@ Feature: Debit cards """ Then I should get a 201 Created status code And the response is valid according to the "debits" schema + + Scenario: Debit a verified bank account + Given I have a verified bank account + When I make a POST request to the link "bank_accounts.debits" with the body: + """ + { "amount": 20000 } + """ + Then I should get a 201 Created status code + And the response is valid according to the "debits" schema + And the fields on these debits match: + """ + { "status": "succeeded" } + """ diff --git a/features/step_definitions/bank_accounts.rb b/features/step_definitions/bank_accounts.rb index 15ff4e1..d8bfa5c 100644 --- a/features/step_definitions/bank_accounts.rb +++ b/features/step_definitions/bank_accounts.rb @@ -22,6 +22,10 @@ amount_1: 1, amount_2: 1 }) + # TODO: fix hax + # client has mutable state, we care about the account, not the verification + + @client.get("/bank_accounts/#{@bank_account_id}") end Given(/^I have a bank account with a verification$/) do From 40f037dc01e9b1eb326268dceef11d4204391e8e Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Sat, 28 Dec 2013 01:12:41 +0000 Subject: [PATCH 073/154] New feature: debits to unverified bank accounts fail. Note that this is different than the old spec, which was incorrect. --- features/debits.feature | 13 +++++++++++++ features/step_definitions/bank_accounts.rb | 11 +++++++++++ 2 files changed, 24 insertions(+) diff --git a/features/debits.feature b/features/debits.feature index dbb984c..7a6fe4d 100644 --- a/features/debits.feature +++ b/features/debits.feature @@ -30,3 +30,16 @@ Feature: Debit cards """ { "status": "succeeded" } """ + + Scenario: Debits to unverified bank accounts fail + Given I have attempted to verify a bank account and failed + When I make a POST request to the link "bank_accounts.debits" with the body: + """ + { "amount": 20000 } + """ + Then I should get a 409 Conflict status code + And the response is valid according to the "debits" schema + And the fields on these debits match: + """ + { "status": "failed" } + """ diff --git a/features/step_definitions/bank_accounts.rb b/features/step_definitions/bank_accounts.rb index d8bfa5c..3f2db71 100644 --- a/features/step_definitions/bank_accounts.rb +++ b/features/step_definitions/bank_accounts.rb @@ -28,6 +28,17 @@ @client.get("/bank_accounts/#{@bank_account_id}") end +Given(/^I have attempted to verify a bank account and failed$/) do + @client.post('/bank_accounts', { + name: "Karl Malone", + account_number: "9900000004", + routing_number: "021000021", + account_type: "checking", + }) + @bank_account_id = @client['bank_accounts']['id'] + @client.add_hydrate(:bank_account_id, @bank_account_id) +end + Given(/^I have a bank account with a verification$/) do step 'I have tokenized a bank account' @client.post("/bank_accounts/#{@bank_account_id}/verifications", {}) From b6bd5a766d8d5ff3b31b3b9f3f99aae548f1b9e8 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Sat, 28 Dec 2013 01:19:21 +0000 Subject: [PATCH 074/154] New feature: debit a customer --- features/debits.feature | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/features/debits.feature b/features/debits.feature index 7a6fe4d..7bfa882 100644 --- a/features/debits.feature +++ b/features/debits.feature @@ -43,3 +43,14 @@ Feature: Debit cards """ { "status": "failed" } """ + + Scenario: Debit a customer + Given I have created a customer + When I make a POST request to the link "customers.debits" with the body: + """ + { "amount": 1234 } + """ + Then I should get a 201 Created status code + And the response is valid according to the "debits" schema + + From 49ece83dc3433b0c64668a0dc037bd44bca88a57 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Sat, 28 Dec 2013 01:23:09 +0000 Subject: [PATCH 075/154] New feature: fail to debit a customer I don't know why this is failing. #460 is tracking it. --- features/debits.feature | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/features/debits.feature b/features/debits.feature index 7bfa882..5c5405c 100644 --- a/features/debits.feature +++ b/features/debits.feature @@ -53,4 +53,12 @@ Feature: Debit cards Then I should get a 201 Created status code And the response is valid according to the "debits" schema - + @failing @gh-460 + Scenario: Failing to debit a customer + Given I have created a customer + When I make a POST request to the link "customers.debits" with the body: + """ + { "amount": 8979 } + """ + Then I should get a 409 Conflict status code + And the response is valid according to the "debits" schema From c7068e60d7a2ff4bba7fa460714211a36fe149be Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 31 Dec 2013 02:31:13 +0000 Subject: [PATCH 076/154] New feature: default funding source. This is failing due to the content-type issue --- features/customers.feature | 38 +++++++++++++++++++++++++ features/step_definitions/http_steps.rb | 8 ++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/features/customers.feature b/features/customers.feature index b29e819..af90bdf 100644 --- a/features/customers.feature +++ b/features/customers.feature @@ -15,3 +15,41 @@ Feature: Customers "name": "Balanced Testing" } """ + + @failing + Scenario: Set the default funding source + When I POST to /cards with the body: + """ + { + "number": "4111 1111 1111 1111", + "expiration_month": 12, + "expiration_year": 2016, + "cvv": "123", + "address": { + "line1": "965 Mission St", + "postal_code": "94103" + } + } + """ + Then I should get a 201 Created status code + And the response is valid according to the "cards" schema + + Given I have created a customer + When I make a PUT request to the link "href" with the body: + """ + { + "links": { + "source": "#{@card_id}", + } + } + """ + Then I should get a 200 OK status code + And the response is valid according to the "customers" schema + And the fields on this customer match: + """ + { + "links": { + "source": "#{@card_id}" + } + } + """ diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 2504de2..4f9ff97 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -2,6 +2,10 @@ @client.verb(verb, @client.hydrater(url)) end +When(/^I (\w+) to (\/\S*?) with the body:$/) do |verb, url, body| + @client.verb(verb, @client.hydrater(url), body) +end + When(/^I make a (\w+) request to (\/\S*?)$/) do |verb, url| step "I #{verb} to #{url}" end @@ -21,8 +25,8 @@ def env @client.post(@client.inject(keys), {}, env) end -When(/^I make a POST request to the link "(.*?)" with the body:$/) do |keys, body| - body = @client.post(@client.hydrater(@client.last_body["links"][keys]), JSON.parse(body), env) +When(/^I make a (\w+) request to the link "(.*?)" with the body:$/) do |verb, keys, body| + body = @client.send(verb.downcase, @client.hydrater(@client.last_body["links"][keys]), JSON.parse(body), env) @credit_id = @client['credits']['id'] rescue nil @cards_id = @client['cards']['id'] rescue nil @debit_url = @client['debits']['href'] rescue nil From 859d9e7a8a36969a665baa0860236e590e423ad7 Mon Sep 17 00:00:00 2001 From: Richie Date: Thu, 2 Jan 2014 13:45:18 -0800 Subject: [PATCH 077/154] New features: customers and orders --- features/customers.feature | 56 +++++++++ features/orders.feature | 235 +++++++++++++++++++++++++++++++++++++ 2 files changed, 291 insertions(+) create mode 100644 features/orders.feature diff --git a/features/customers.feature b/features/customers.feature index af90bdf..45b2b18 100644 --- a/features/customers.feature +++ b/features/customers.feature @@ -53,3 +53,59 @@ Feature: Customers } } """ + + @failing + Scenario: Set the default destination + When I make a PATCH request to the link "href" with the body: + """ + { + "op": "replace", + "path": "/customers/0/links/destination", + "value": "#{@bank_accounts_id}" + } + """ + Then I should get a 200 OK status code + And the response is valid according to the "customers" schema + And the fields on this customer match: + """ + { + "links": { "destination": "#{@bank_accounts_id}" } + } + """ + + @failing + Scenario: Underwrite a customer + When I Post to /customers with the body: + """ + { + "name": "Henry Ford" + } + """ + Then I should get a 201 Created status code + And the response is valid according to the "customers" schema + And the fields on this customer match: + """ + { + "merchant_status": "need-more-information" + } + """ + + When I make a PUT request to the link "href" with the body: + """ + { + "dob_month": 7, + "dob_year": 1963, + "address": { + "postal_code": "48120" + } + } + """ + Then I should get a 200 OK status code + And the response is valid according to the "customers" schema + And the fields on this customer match: + """ + { + "merchant_status": "underwritten" + } + """ + diff --git a/features/orders.feature b/features/orders.feature new file mode 100644 index 0000000..a0c9cae --- /dev/null +++ b/features/orders.feature @@ -0,0 +1,235 @@ +Feature: Orders + + @failing + Scenario: Create an order + Given I have an underwritten customer + When I make a POST request to /customers/:customer_id/orders + + Then I should get a 201 Created status code + And the response is valid according to the "orders" schema + + @failing + Scenario: Checking escrow of order after creating a debit + Given I have tokenized a customer card + And I make a POST request to the link "cards.debits" with the body: + """ + { + "order": "#{@orders_id}", + "amount": 1234 + } + """ + + When I make a GET request to the link /orders/:order_id + Then I should get a 200 OK status code + And the response is valid according to the "orders" schema + """ + { + "amount_escrowed": 1234 + } + """ + + @failing + Scenario: Checking escrow of order after creating a credit + Given I have sufficient funds in my marketplace + And I have tokenized a bank account + And I POST to /bank_accounts/:bank_account_id/credits with the JSON API body: + """ + { + "order": "#{@orders_id}", + "amount": 1234 + } + """ + When I make a GET request to the link /orders/:order_id + Then I should get a 200 OK status code + And the response is valid according to the "orders" schema + """ + { + "amount_escrowed": 0 + } + """ + + + @failing + Scenario: Orders cannot be credited more than escrow balance + Given I have tokenized a customer card + And I make a POST request to the link "cards.debits" with the body: + """ + { + "order": "#{@orders_id}", + "amount": 1234 + } + """ + When I make a GET request to the link /orders/:order_id + Then I should get a 200 OK status code + And the response is valid according to the "orders" schema + """ + { + "amount_escrowed": 1234 + } + """ + And I POST to /bank_accounts/:bank_account_id/credits with the JSON API body: + """ + { + "order": "#{@orders_id}", + "amount": 2000 + } + """ + Then I should get a 409 status code + And the response is valid according to the "errors" schema + And the fields on this error match: + """ + { + "category_code": "account-insufficient-funds" + } + """ + + @failing + Scenario: Create a refund + Given I have tokenized a customer card + And I have debited that card + + When I make a POST request to the link "debits.refunds" + And I make a GET request to the link /orders/:order_id + Then I should get a 200 OK status code + And the response is valid according to the "order" schema + """ + { + "amount_escrowed": 0 + } + """ + + + @failing + Scenario: Create a reversal + Given I have tokenized a customer card + And I make a POST request to /customers/:customer_id/orders with the body + """ + "order": "#{@orders_id}", + "amount": 1234 + """ + And I have tokenized a bank account + And I make a POST request to the link "bank_accounts.credits" with the body: + """ + "order": "#{@orders_id}", + "amount": 1234 + """ + + When I make a POST request to the link "credits.reversals" + Then I should get a 201 Created status code + And the response is valid according to the "reversals" schema + + When I make a GET request to the link /orders/:order_id + Then I should get a 200 OK status code + And the response is valid according to the "orders" schema + """ + { + "amount_escrowed": 0 + } + """ + + + @failing + Scenario: Create a failed refund when insufficient funds are in order escrow + Given I have tokenized a card + When I make a POST request to the link "cards.debits" with the body: + """ + { + "order": "#{@orders_id}", + "amount": 1234 + } + """ + And I have tokenized a bank account + Then I make a POST request to the link "bank_accounts.credits" with the body: + """ + "order": "#{@orders_id}", + "amount": 1234 + """ + When I make a POST request to the link "debits.refunds" + Then I should get a 409 status code + And the response is valid according to the "errors" schema + And the fields on this error match: + """ + { + "category_code": "account-insufficient-funds" + } + """ + + @failing + Scenario: Transactions should inherit the description of the order by default + Given I have tokenized a bank account + When I make a POST request to the link "customers.orders" with the body: + """ + { + "description": "Beats by Dr. Dre" } + } + """ + + Then I should get a 201 Created status code + And the response is valid according to the "orders" schema + """ + { + { "description": "Beats by Dr. Dre" } + } + """ + + And I make a POST request to the link "customers.debits" with the body: + """ + { + "amount": 10000, + "order": "#{@orders_id}", + } + """ + + Then I should get a 201 Created status code + And the response is valid according to the "debits" schema + """ + { + "description": "Beats by Dr. Dre" } + } + """ + + And I make a POST request to the link "customers.credits" with the body: + """ + { + "amount": 10000, + "order": "#{@orders_id}", + } + """ + Then I should get a 201 Created status code + And the response is valid according to the "debits" schema + """ + { + { "description": "Beats by Dr. Dre"} + } + """ + + @failing + Scenario: Crediting an unverified merchant will result in failure + Given I have an unverified customer with a tokenized card + And I make a POST request to 'customers.orders' + + And I make a POST request to the link "cards.debits" with the body: + """ + { + "order": "#{@orders_id}", + "amount": 1234 + } + """ + Then I should get a 201 Created status code + + And I make a POST request to the link "bank_accounts.credits" with the body: + """ + { + "amount": 1234, + "order": "#{@orders_id}", + } + """ + Then I should get a 409 status code + And the response is valid according to the "errors" schema + And the fields on this error match: + """ + { + "description": "Order requires that merchant CU[a-zA-Z0-9]{16,32} be underwritten.", + "category_code": "order-kyc" + } + """ From bf531fcfe94c49afd44bcf421425d2c3d9b99183 Mon Sep 17 00:00:00 2001 From: Richie Date: Thu, 2 Jan 2014 13:45:18 -0800 Subject: [PATCH 078/154] Convert more scenarios --- features/checkout_flow.feature | 369 +++++++++++++++++++++++++++++++++ features/credits.feature | 49 +++++ 2 files changed, 418 insertions(+) create mode 100644 features/checkout_flow.feature create mode 100644 features/credits.feature diff --git a/features/checkout_flow.feature b/features/checkout_flow.feature new file mode 100644 index 0000000..b51fede --- /dev/null +++ b/features/checkout_flow.feature @@ -0,0 +1,369 @@ +Feature: Credit cards + + @failing + Scenario: Canceling an order + Given I have an order + Then I make a GET request to the link /orders/:order_id + Then I should get a 200 OK status code + And the response is valid according to the "orders" schema + """ + { + "description": "Catherine Malandrino Black Top" + } + """ + + Then I make a GET request to "orders.debits" + Then I should get a 200 OK status code + And the response is valid according to the "debits" schema + """ + { + "amount": 10000, + "description": "Catherine Malandrino Black Top" + } + """ + + Then I make a POST request to "debits.refunds" + Then I should get a 201 OK status code + And the response is valid according to the "refunds" schema + """ + { + "amount": 10000, + "description": "Catherine Malandrino Black Top" + } + """ + + Then I make a GET request to the link /orders/:order_id + Then I should get a 200 OK status code + And the response is valid according to the "orders" schema + """ + { + "amount": 0, + "amount_escrowed": 0, + "currency": "USD", + "description": "Catherine Malandrino Black Top" + } + """ + + @failing + Scenario: Existing buyer makes a purchase with a new card + When I make a GET request to /customers/:customer_id + Then I should get a 200 OK status code + And the response is valid according to the "customers" schema + + Then I make a GET request to "customers.source" + Then I should get a 200 OK status code + And the response is valid according to the "cards" schema + + Then I make a POST request to /cards with the JSON API body: + """ + { + "name": "Darius the Great", + "number": "4111111111111111", + "expiration_month": 12, + "expiration_year": 2016, + "cvv": "123", + "address": { + "line1": "965 Mission St", + "line2": "Suite 425", + "city": "San Francisco", + "state": "CA", + "postal_code": "94103" + } + } + """ + Then I should get a 201 CREATED status code + And the response is valid according to the "cards" schema + + When I make a GET request to /cards/:cards_id + Then I should get a 200 OK status code + And the response is valid according to the "cards" schema + """ + { + "avs_street_match": "yes", + "avs_postal_match": "yes", + "cvv_match": "yes" + } + """ + + When I make a PATCH request to /customers/:customer_id with the JSON API body: + """ + { + "op": "replace", + "path": "/customers/0/links/source", + "value": "#{@cards_id}" + } + """ + Then I should get a 200 OK status code + And the response is valid according to the "customers" schema + """ + { + "links": { "source": "#{@cards_id}" } + } + """ + + When I make a POST request to /customers/:customer_id/orders with the body + """ + { + "description": "Catherine Malandrino Black Top", + "delivery_address": { + "line1": "965 Mission St", + "line2": "Suite 425", + "city": "San Francisco", + "state": "CA", + "postal_code": "94103" + }, + "meta": { + "listing": "https://www.vaunte.com/items/catherine-malandrino-black-top-42" + } + } + """ + Then I should get a 201 CREATED status code + And the response is valid according to the "orders" schema + """ + { + "links":{ "merchant": "#{@customers_id}" } + } + """ + + When I make a POST request to "customers.debits" with the body + """ + { + "amount": 10000, + "order": "#{@orders_id}", + "appears_on_statement_as": "Vaunte-Alice Ryan" + } + """ + Then I should get a 201 CREATED status code + And the response is valid according to the "debits" schema + """ + { + "description": "Catherine Malandrino Black Top", + "appears_on_statement_as": "BAL*Vaunte-Alice Ryan", + "status": "succeeded", + "links": { + "order": "#{@orders_id}", + } + } + """ + + When I make a PUT request to /orders/:order_id with the JSON API body: + """ + { + "meta": { + "listing": "https://www.vaunte.com/items/catherine-malandrino-black-top-42", + "courier": "usps", + "tracking_number": "9405510899359008595488" + } + } + """ + Then I should get a 200 OK status code + And the response is valid according to the "orders" schema + """ + { + "meta": { + "listing": "https://www.vaunte.com/items/catherine-malandrino-black-top-42", + "courier": "usps", + "tracking_number": "9405510899359008595488" + } + } + """ + + @failing + Scenario: Existing buyer makes a purchase with an existing card + When I make a GET request to /customers/:customer_id + Then I should get a 200 OK status code + And the response is valid according to the "customers" schema + + Then I make a GET request to "customers.source" + Then I should get a 200 OK status code + And the response is valid according to the "cards" schema + + When I make a POST request to /customers/:customer_id/orders with the body: + """ + { + "description": "Catherine Malandrino Black Top", + "delivery _address": { + "line1": "965 Mission St", + "line2": "Suite 425", + "city": "San Francisco", + "state": "CA", + "postal_code": "94103" + }, + "meta": { + "listing": "https://www.vaunte.com/items/catherine-malandrino-black-top-42" + } + } + """ + Then I should get a 201 CREATED status code + And the response is valid according to the "orders" schema + """ + { + "links":{ "merchant": "#{@customers_id}" } + } + """ + + When I make a POST request to "customers.debits" with the body + """ + { + "amount": 10000, + "order": "#{@orders_id}", + "appears_on_statement_as": "Vaunte-Alice Ryan" + } + """ + Then I should get a 201 CREATED status code + And the response is valid according to the "debits" schema + """ + { + "description": "Catherine Malandrino Black Top", + "appears_on_statement_as": "BAL*Vaunte-Alice Ryan", + "status": "succeeded", + "links": { + "order": "#{@orders_id}", + } + } + """ + + When I make a PUT request to /orders/:order_id with the JSON API body: + """ + { + "meta": { + "listing": "https://www.vaunte.com/items/catherine-malandrino-black-top-42", + "courier": "usps", + "tracking_number": "9405510899359008595488" + } + } + """ + Then I should get a 200 OK status code + And the response is valid according to the "orders" schema + """ + { + "meta": { + "listing": "https://www.vaunte.com/items/catherine-malandrino-black-top-42", + "courier": "usps", + "tracking_number": "9405510899359008595488" + } + } + """ + + @failing + Scenario: New buyer makes a purchase + When I make a POST request to /cards with the JSON API body: + """ + { + "name": "Darius the Great", + "number": "4111111111111111", + "expiration_month": 12, + "expiration_year": 2016, + "cvv": "123", + "address": { + "line1": "965 Mission St", + "line2": "Suite 425", + "city": "San Francisco", + "state": "CA", + "postal_code": "94103" + } + } + """ + Then I should get a 201 CREATED status code + And the response is valid according to the "cards" schema + + When I make a GET request to /cards/:cards_id + Then I should get a 200 OK status code + And the response is valid according to the "cards" schema + """ + { + "avs_street_match": "yes", + "avs_postal_match": "yes", + "cvv_match": "yes" + } + """ + + When I make a POST request to /customers + """ + { + "name": "Darius the Great", + "email": "darius.great@gmail.com", + "source": "#{@cards_id}", + "meta": { + "ip_address": "174.240.15.249" + } + } + """ + Then I should get a 201 OK status code + And the response is valid according to the "customers" schema + """ + { + "links": { + "source": "#{@cards_id}", + "destination": null, + } + } + """ + + When I make a POST request to /customers/:customer_id/orders with the body + """ + { + "description": "Catherine Malandrino Black Top", + "delivery_address": { + "line1": "965 Mission St", + "line2": "Suite 425", + "city": "San Francisco", + "state": "CA", + "postal_code": "94103" + }, + "meta": { + "listing": "https://www.vaunte.com/items/catherine-malandrino-black-top-42" + } + } + """ + Then I should get a 201 CREATED status code + And the response is valid according to the "orders" schema + """ + { + "links":{ "merchant": "#{@customers_id}" } + } + """ + + When I make a POST request to "customers.debits" with the body + """ + { + "amount": 10000, + "order": "#{@orders_id}", + "appears_on_statement_as": "Vaunte-Alice Ryan" + } + """ + Then I should get a 201 CREATED status code + And the response is valid according to the "debits" schema + """ + { + "description": "Catherine Malandrino Black Top", + "appears_on_statement_as": "BAL*Vaunte-Alice Ryan", + "status": "succeeded", + "links": { + "order": "#{@orders_id}", + } + } + """ + + + When I make a PUT request to /orders/:order_id with the JSON API body: + """ + { + "meta": { + "listing": "https://www.vaunte.com/items/catherine-malandrino-black-top-42", + "courier": "usps", + "tracking_number": "9405510899359008595488" + } + } + """ + Then I should get a 200 OK status code + And the response is valid according to the "orders" schema + """ + { + "meta": { + "listing": "https://www.vaunte.com/items/catherine-malandrino-black-top-42", + "courier": "usps", + "tracking_number": "9405510899359008595488" + } + } + """ diff --git a/features/credits.feature b/features/credits.feature new file mode 100644 index 0000000..b6a0f72 --- /dev/null +++ b/features/credits.feature @@ -0,0 +1,49 @@ +Feature: Credit cards + + @failing + Scenario: Crediting a deleted card leads to failure + Given I have tokenized a card + When I make a DELETE request to the link "href" + Then I should get a 204 status code + + When I make a POST request to /cards/:cards_id/credits + Then I should get a 404 status code + + And the response is valid according to the "errors" schema + And the fields on this error match: + """ + { + "category_code": "not-found" + } + """ + + @failing + Scenario: Credit a customer + Given I have a customer with a tokenized bank account + When I POST to /customers/:customers_id/credit with the JSON API body: + """ + { + "amount": 500 + } + """ + Then I should get a 201 Created status code + And the response is valid according to the "debits" schema + + @failing + Scenario: Crediting without a funding source leads to failure + Given I have a customer without an associated funding instrument + When I POST to /customers/:customers_id/credit with the JSON API body: + """ + { + "amount": 1234 + } + """ + Then I should get a 409 status code + And the response is valid according to the "errors" schema + And the fields on this error match: + """ + { + "category_code": "no-funding-destination" + } +` """ + From 3830fea2b47113ec091ce3196b03aec11058a382 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Mon, 6 Jan 2014 09:02:47 -0800 Subject: [PATCH 079/154] fixes error scenario and issue #459 --- features/refunds.feature | 3 +- fixtures/_models/debit.json | 2 +- fixtures/_models/error.json | 187 ++++++++++++++++++++++++++++++++++ fixtures/errors.json | 193 ++---------------------------------- 4 files changed, 199 insertions(+), 186 deletions(-) create mode 100644 fixtures/_models/error.json diff --git a/features/refunds.feature b/features/refunds.feature index 0c42036..5a1c387 100644 --- a/features/refunds.feature +++ b/features/refunds.feature @@ -1,5 +1,5 @@ Feature: Refunds - + Scenario: Create a refund Given I have tokenized a customer card And I have debited that card @@ -8,7 +8,6 @@ Feature: Refunds Then I should get a 201 Created status code And the response is valid according to the "refunds" schema - @failing @gh-459 Scenario: Create a failed refund Given I have tokenized a customer card And I have debited that card diff --git a/fixtures/_models/debit.json b/fixtures/_models/debit.json index bcdc5aa..561500f 100644 --- a/fixtures/_models/debit.json +++ b/fixtures/_models/debit.json @@ -106,4 +106,4 @@ "meta", "links" ] -} +} \ No newline at end of file diff --git a/fixtures/_models/error.json b/fixtures/_models/error.json new file mode 100644 index 0000000..7c4fb3c --- /dev/null +++ b/fixtures/_models/error.json @@ -0,0 +1,187 @@ +{ + "type": "object", + "properties": { + "status": { + "type": "string" + }, + "category_code": { + "type": "string", + "enum": [ + "account-insufficient-funds", + "authorization-failed", + "card-declined", + "funding-destination-declined", + "account-already-merchant", + "address-verification-failed", + "authorization-expired", + "bank-account-already-associated", + "bank-account-authentication-already-exists", + "bank-account-authentication-failed", + "bank-account-authentication-not-pending", + "bank-account-not-associated", + "bank-account-not-valid", + "business-kyc", + "business-principal-kyc", + "cannot-associate-bank-account", + "cannot-associate-card", + "cannot-associate-merchant-with-account", + "cannot-capture-authorization", + "cannot-void-authorization", + "capture-void-attempt", + "card-already-funding-src", + "card-not-associated", + "card-not-valid", + "card-not-validated", + "credit-already-reversed", + "debit-already-refunded", + "debit-not-found", + "duplicate-email-address", + "funding-destination-already-associated", + "funding-destination-cannot-reverse", + "funding-destination-not-associated", + "funding-destination-not-creditable", + "funding-source-already-associated", + "funding-source-not-associated", + "funding-source-not-authorizable", + "funding-source-not-debitable", + "funding-source-not-hold", + "funding-source-not-refundable", + "hold-not-associated-account", + "hold-not-associated-marketplace", + "identity-verification-error", + "insufficient-funds", + "marketplace-already-created", + "marketplace-cannot-prepay-fees", + "multiple-debits", + "no-funding-destination", + "no-funding-source", + "person-kyc", + "precog-logical-error", + "refund-insufficient-funds", + "reverse-void-attempt", + "unexpected-payload", + "bank-account-authentication-forbidden", + "cannot-reverse-credit", + "incomplete-account-info", + "invalid-amount", + "invalid-bank-account-number", + "invalid-reversal-amount", + "invalid-routing-number", + "precog-bad-request", + "refund-invalid-debit-state", + "not-found", + "patch-test-failed", + "request", + "order-kyc", + "method-not-allowed" + ] + }, + "description": { + "__example": "Human readable error message about what went wrong. Your request id is OHMasdfasdfasdfasdf", + "anyOf": [ + { + "type": "string", + "enum": [ + "Account balance is insufficient to issue this debit", + "An account with the given email address already exists.", + "Bank account authentication amounts do not match.", + "Bank account authentication amounts do not match.", + "Bank account authentication is not allowed.", + "Bank account authentication is not in state ``pending``.", + "Credit has already been fully reversed.", + "Credit to reversal must have ``succeeded``.", + "Debit has already been fully refunded.", + "Marketplace balance is insufficient to issue this credit", + "Marketplace escrow balance is insufficient to issue this credit.", + "Marketplace escrow balance is insufficient to issue this refund.", + "Marketplace has already been created.", + "No buyer or merchant info was provided. Either \"merchant/merchant_uri\" or\n\"card/card_uri\" fields must be present.", + "Refund amount cannot be greater than the amount of the original debit.", + "Reverse amount cannot be greater than the amount of the original credit.", + "State of debit to refund must be ``succeeded``.", + "The account has no valid funding destinations.", + "The account has no valid funding sources.", + "The account is already a merchant.", + "The bank account is already associated with an account -- it cannot be\nassociated.", + "The bank account is not associated with the given account.", + "The bank account was tokenized on a different marketplace than the one\nused for this request.", + "The card could not be validated -- either the card number or security code\nmay be wrong.", + "The card is already associated with an account -- it cannot be associated\nagain.", + "The card is not associated with the given account.", + "The card was tokenized on a different marketplace than the one used for\nthis request.", + "The given funding destination cannot have a credit created against it.", + "The given funding destination cannot reverse.", + "The given funding destination is already associated with an account.", + "The given funding destination is not associated with the account.", + "The given funding source cannot be refunded.", + "The given funding source cannot have a debit created against it.", + "The given funding source cannot have a hold created against it.", + "The given funding source cannot have an authorization created against\nit. Authorizations are only valid for card-type funding sources.", + "The given funding source is already associated with an account.", + "The given funding source is not associated with an account.", + "The merchant information was created on a different marketplace than the\none used for this request.", + "The processor did not accept the transaction.", + "The processor did not accept this hold.", + "The routing number provided for the bank account was invalid.", + "This address could not be verified.", + "This bank account has already been marked as invalid/deactivated. It cannot\nbe used again.", + "This bank account number is not alpha numeric (ascii). It can not be used.", + "This card has already been marked as invalid/deactivated. It cannot be used\nagain.", + "This card was declined by the processor.", + "This hold has already been captured or voided, and cannot be captured.", + "This hold has already been captured or voided, and cannot be voided.", + "This hold has already been captured, which cannot be reversed.", + "This hold has already been voided, which cannot be reversed.", + "This hold has already expired.", + "This hold is not associated with this account.", + "This hold is not associated with this marketplace.", + "Order escrow balanced is insufficient to issue this credit.", + "Order escrow balanced is insufficient to issue this refund.", + "Order requires merchant be underwritten." + ] + }, + { + "type": "string", + "pattern": ".* Your request id is OHM[a-zA-Z0-9]*\\." + }, + { + "type": "null" + } + ] + }, + "additional": {}, + "extras": {}, + "status_code": { + "type": "integer", + "enum": [ + 400, + 402, + 403, + 405, + 404, + 409 + ] + }, + "category_type": { + "type": "string", + "enum": [ + "request", + "banking", + "logical" + ] + }, + "request_id": { + "type": "string", + "pattern": "OHM[a-zA-Z0-9]{16,34}" + } + }, + "required": [ + "status", + "category_code", + "description", + "status_code", + "category_type", + "request_id" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/fixtures/errors.json b/fixtures/errors.json index 9b8b964..c0135da 100644 --- a/fixtures/errors.json +++ b/fixtures/errors.json @@ -1,189 +1,16 @@ { "type": "object", "properties": { - "status": { - "type": "string", - "required": true - }, - "category_code": { - "type": "string", - "enum": [ - "account-insufficient-funds", - "authorization-failed", - "card-declined", - "funding-destination-declined", - "account-already-merchant", - "address-verification-failed", - "authorization-expired", - "bank-account-already-associated", - "bank-account-authentication-already-exists", - "bank-account-authentication-failed", - "bank-account-authentication-not-pending", - "bank-account-not-associated", - "bank-account-not-valid", - "business-kyc", - "business-principal-kyc", - "cannot-associate-bank-account", - "cannot-associate-card", - "cannot-associate-merchant-with-account", - "cannot-capture-authorization", - "cannot-void-authorization", - "capture-void-attempt", - "card-already-funding-src", - "card-not-associated", - "card-not-valid", - "card-not-validated", - "credit-already-reversed", - "debit-already-refunded", - "debit-not-found", - "duplicate-email-address", - "funding-destination-already-associated", - "funding-destination-cannot-reverse", - "funding-destination-not-associated", - "funding-destination-not-creditable", - "funding-source-already-associated", - "funding-source-not-associated", - "funding-source-not-authorizable", - "funding-source-not-debitable", - "funding-source-not-hold", - "funding-source-not-refundable", - "hold-not-associated-account", - "hold-not-associated-marketplace", - "identity-verification-error", - "insufficient-funds", - "marketplace-already-created", - "marketplace-cannot-prepay-fees", - "multiple-debits", - "no-funding-destination", - "no-funding-source", - "person-kyc", - "precog-logical-error", - "refund-insufficient-funds", - "reverse-void-attempt", - "unexpected-payload", - "bank-account-authentication-forbidden", - "cannot-reverse-credit", - "incomplete-account-info", - "invalid-amount", - "invalid-bank-account-number", - "invalid-reversal-amount", - "invalid-routing-number", - "precog-bad-request", - "refund-invalid-debit-state", - "not-found", - "patch-test-failed", - "request", - "order-kyc", - "method-not-allowed" - ], - "required": true - }, - "description": { - "__example": "Human readable error message about what went wrong. Your request id is OHMasdfasdfasdfasdf", - "anyOf": [ - { - "type": "string", - "enum": [ - "Account balance is insufficient to issue this debit", - "An account with the given email address already exists.", - "Bank account authentication amounts do not match.", - "Bank account authentication amounts do not match.", - "Bank account authentication is not allowed.", - "Bank account authentication is not in state ``pending``.", - "Credit has already been fully reversed.", - "Credit to reversal must have ``succeeded``.", - "Debit has already been fully refunded.", - "Marketplace balance is insufficient to issue this credit", - "Marketplace escrow balance is insufficient to issue this credit.", - "Marketplace escrow balance is insufficient to issue this refund.", - "Marketplace has already been created.", - "No buyer or merchant info was provided. Either \"merchant/merchant_uri\" or\n\"card/card_uri\" fields must be present.", - "Refund amount cannot be greater than the amount of the original debit.", - "Reverse amount cannot be greater than the amount of the original credit.", - "State of debit to refund must be ``succeeded``.", - "The account has no valid funding destinations.", - "The account has no valid funding sources.", - "The account is already a merchant.", - "The bank account is already associated with an account -- it cannot be\nassociated.", - "The bank account is not associated with the given account.", - "The bank account was tokenized on a different marketplace than the one\nused for this request.", - "The card could not be validated -- either the card number or security code\nmay be wrong.", - "The card is already associated with an account -- it cannot be associated\nagain.", - "The card is not associated with the given account.", - "The card was tokenized on a different marketplace than the one used for\nthis request.", - "The given funding destination cannot have a credit created against it.", - "The given funding destination cannot reverse.", - "The given funding destination is already associated with an account.", - "The given funding destination is not associated with the account.", - "The given funding source cannot be refunded.", - "The given funding source cannot have a debit created against it.", - "The given funding source cannot have a hold created against it.", - "The given funding source cannot have an authorization created against\nit. Authorizations are only valid for card-type funding sources.", - "The given funding source is already associated with an account.", - "The given funding source is not associated with an account.", - "The merchant information was created on a different marketplace than the\none used for this request.", - "The processor did not accept the transaction.", - "The processor did not accept this hold.", - "The routing number provided for the bank account was invalid.", - "This address could not be verified.", - "This bank account has already been marked as invalid/deactivated. It cannot\nbe used again.", - "This bank account number is not alpha numeric (ascii). It can not be used.", - "This card has already been marked as invalid/deactivated. It cannot be used\nagain.", - "This card was declined by the processor.", - "This hold has already been captured or voided, and cannot be captured.", - "This hold has already been captured or voided, and cannot be voided.", - "This hold has already been captured, which cannot be reversed.", - "This hold has already been voided, which cannot be reversed.", - "This hold has already expired.", - "This hold is not associated with this account.", - "This hold is not associated with this marketplace.", - "Order escrow balanced is insufficient to issue this credit.", - "Order escrow balanced is insufficient to issue this refund.", - "Order requires merchant be underwritten." - ] - }, - { - "type": "string", - "pattern": ".* Your request id is OHM[a-zA-Z0-9]*\\." - }, - { - "type": "null" - } - ], - "required": true - }, - "additional": { - "required": false - }, - "extras": { - "required": false - }, - "status_code": { - "type": "integer", - "enum": [ - 400, - 402, - 403, - 405, - 404, - 409 - ], - "required": true - }, - "category_type": { - "type": "string", - "enum": [ - "request", - "banking", - "logical" - ], - "required": true - }, - "request_id": { - "type": "string", - "pattern": "OHM[a-zA-Z0-9]{16,34}", - "required": true + "errors": { + "items": { + "$ref": "_models/error.json" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true } }, - "additionalProperties": false + "required": [ + "errors" + ] } From f528307a2f916b0a773ef74f68ae2fea7d8be690 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Mon, 6 Jan 2014 09:43:30 -0800 Subject: [PATCH 080/154] number of minor fixes --- features/credits.feature | 16 +++++++--------- features/debits.feature | 3 +-- features/reversals.feature | 5 ++--- features/step_definitions/bank_accounts.rb | 10 ++++++++++ features/step_definitions/customers.rb | 10 +++++++++- features/step_definitions/http_steps.rb | 12 +++++++++--- 6 files changed, 38 insertions(+), 18 deletions(-) diff --git a/features/credits.feature b/features/credits.feature index b6a0f72..b96666a 100644 --- a/features/credits.feature +++ b/features/credits.feature @@ -1,4 +1,5 @@ -Feature: Credit cards +Feature: Credits + Credits are used for sending money to a customer @failing Scenario: Crediting a deleted card leads to failure @@ -17,22 +18,20 @@ Feature: Credit cards } """ - @failing Scenario: Credit a customer Given I have a customer with a tokenized bank account - When I POST to /customers/:customers_id/credit with the JSON API body: + When I POST to /customers/:customer_id/credits with the JSON API body: """ { "amount": 500 } """ Then I should get a 201 Created status code - And the response is valid according to the "debits" schema + And the response is valid according to the "credits" schema - @failing Scenario: Crediting without a funding source leads to failure - Given I have a customer without an associated funding instrument - When I POST to /customers/:customers_id/credit with the JSON API body: + Given I have created a customer + When I POST to /customers/:customer_id/credits with the JSON API body: """ { "amount": 1234 @@ -45,5 +44,4 @@ Feature: Credit cards { "category_code": "no-funding-destination" } -` """ - + """ diff --git a/features/debits.feature b/features/debits.feature index 5c5405c..9af114c 100644 --- a/features/debits.feature +++ b/features/debits.feature @@ -53,9 +53,8 @@ Feature: Debit cards Then I should get a 201 Created status code And the response is valid according to the "debits" schema - @failing @gh-460 Scenario: Failing to debit a customer - Given I have created a customer + Given I have created a customer without a card and bank account When I make a POST request to the link "customers.debits" with the body: """ { "amount": 8979 } diff --git a/features/reversals.feature b/features/reversals.feature index bb7d390..4ef31c6 100644 --- a/features/reversals.feature +++ b/features/reversals.feature @@ -1,5 +1,5 @@ Feature: Reversals - + Scenario: Reverse a credit. Given I have tokenized a bank account @@ -14,7 +14,6 @@ Feature: Reversals Then I should get a 201 Created status code And the response is valid according to the "reversals" schema - @failing Scenario: Failed reversal You can't reverse a particular amount. @@ -33,7 +32,7 @@ Feature: Reversals Then I should get a 400 status code And the response is valid according to the "errors" schema - @failing + @failing Scenario: Reverse a bank account credit When I POST to /credits with the JSON API body: diff --git a/features/step_definitions/bank_accounts.rb b/features/step_definitions/bank_accounts.rb index 3f2db71..e3350fd 100644 --- a/features/step_definitions/bank_accounts.rb +++ b/features/step_definitions/bank_accounts.rb @@ -45,3 +45,13 @@ @bank_account_verification_id = @client['bank_account_verifications']['id'] @client.add_hydrate :bank_account_verification_id, @bank_account_verification_id end + +Given(/^I have a customer with a tokenized bank account$/) do + step 'I have created a customer' + step 'I have tokenized a bank account' + @client.put("/bank_accounts/#{@bank_account_id}", { + links: { + customer: @customer_id + } + }) +end diff --git a/features/step_definitions/customers.rb b/features/step_definitions/customers.rb index e46bf67..085f080 100644 --- a/features/step_definitions/customers.rb +++ b/features/step_definitions/customers.rb @@ -27,7 +27,7 @@ value: @customer_id, }] ) - + # associate their card so that they have a funding source @client.patch(@customer_url, [{ @@ -43,6 +43,14 @@ @client.get(@customer_url) end +Given(/^I have created a customer without a card and bank account$/) do + @client.post('/customers', {}) + @customer_id = @client['id'] + @client.add_hydrate :customer_id, @customer_id + + @customer_url = @client['customers']['href'] +end + Given(/^I have created more than one customer$/) do 2.times { step "I have created a customer" } diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 4f9ff97..cb5ec67 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -23,7 +23,7 @@ def env When(/^I make a POST request to the href "(.*?)"$/) do |keys| @client.post(@client.inject(keys), {}, env) -end +end When(/^I make a (\w+) request to the link "(.*?)" with the body:$/) do |verb, keys, body| body = @client.send(verb.downcase, @client.hydrater(@client.last_body["links"][keys]), JSON.parse(body), env) @@ -31,13 +31,13 @@ def env @cards_id = @client['cards']['id'] rescue nil @debit_url = @client['debits']['href'] rescue nil body -end +end When(/^I make a POST request to the link "(.*?)"$/) do |keys| body = @client.post(@client.hydrater(@client.last_body["links"][keys]), {}, env) @credit_id = @client['credits']['id'] rescue nil body -end +end When(/^I POST to (\/.*) without my secret key with the JSON API body:$/) do |url, body| # use for tokenizing cards and bank accounts @@ -124,6 +124,12 @@ def checker(from, of, nesting) assert @client.last_body[name].size >= 2, "There were not more than two #{name}" end + +Then(/^debug$/) do + puts "HTTP status code: #{@client.last_code}" + puts "HTTP body: #{@client.last_body}" +end + # TODO: move? Before do |scenario| From 7736c9e711ac35673ea988e06a7bb9b6897d0635 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 6 Jan 2014 22:19:53 +0000 Subject: [PATCH 081/154] Some movement toward #467 --- features/credits.feature | 4 ++-- features/step_definitions/http_steps.rb | 9 +++++---- lib/balanced/tiny_client.rb | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/features/credits.feature b/features/credits.feature index b96666a..0df2486 100644 --- a/features/credits.feature +++ b/features/credits.feature @@ -1,10 +1,10 @@ Feature: Credits Credits are used for sending money to a customer - @failing + @failing @gh-467 Scenario: Crediting a deleted card leads to failure Given I have tokenized a card - When I make a DELETE request to the link "href" + When I make a DELETE request to the href "href" Then I should get a 204 status code When I make a POST request to /cards/:cards_id/credits diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index cb5ec67..3042977 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -21,8 +21,9 @@ def env end -When(/^I make a POST request to the href "(.*?)"$/) do |keys| - @client.post(@client.inject(keys), {}, env) +When(/^I make a (\w+) request to the href "(.*?)"$/) do |verb, keys| + link = @client[keys] || @client.inject(keys) + @client.send(verb.downcase, link, {}, env) end When(/^I make a (\w+) request to the link "(.*?)" with the body:$/) do |verb, keys, body| @@ -33,8 +34,8 @@ def env body end -When(/^I make a POST request to the link "(.*?)"$/) do |keys| - body = @client.post(@client.hydrater(@client.last_body["links"][keys]), {}, env) +When(/^I make a (\w+) request to the link "(.*?)"$/) do |verb, keys| + body = @client.send(verb.downcase, @client.hydrater(@client.last_body["links"][keys]), {}, env) @credit_id = @client['credits']['id'] rescue nil body end diff --git a/lib/balanced/tiny_client.rb b/lib/balanced/tiny_client.rb index fe366fb..4ac12ad 100644 --- a/lib/balanced/tiny_client.rb +++ b/lib/balanced/tiny_client.rb @@ -83,7 +83,7 @@ def get(endpoint) verb 'GET', endpoint end - def delete(endpoint) + def delete(endpoint, body=nil, env={}) verb 'DELETE', endpoint end From d0d02c142ec8d0de6cd11ae98475abac179727cc Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 6 Jan 2014 22:44:59 +0000 Subject: [PATCH 082/154] Passing: reverse a credit on a bank account. I had the wrong fixture data, so it was creating a failed credit, rather than a succeeded one. --- features/reversals.feature | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/features/reversals.feature b/features/reversals.feature index 4ef31c6..78281b1 100644 --- a/features/reversals.feature +++ b/features/reversals.feature @@ -32,7 +32,6 @@ Feature: Reversals Then I should get a 400 status code And the response is valid according to the "errors" schema - @failing Scenario: Reverse a bank account credit When I POST to /credits with the JSON API body: @@ -41,7 +40,7 @@ Feature: Reversals "amount": 1234, "destination": { "name": "Michael Jordan", - "account_number": "9900000004", + "account_number": "9900000095", "routing_number": "021000021", "account_type": "checking" } From 59337e06b507cfe097ac4c613e73fd1a609bfd29 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 6 Jan 2014 22:51:26 +0000 Subject: [PATCH 083/154] remove 'set the default funding source In 1.1, only instruments can be credited, so a default funding source doesn't make sense. See #460 --- features/customers.feature | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/features/customers.feature b/features/customers.feature index 45b2b18..6912af5 100644 --- a/features/customers.feature +++ b/features/customers.feature @@ -15,44 +15,6 @@ Feature: Customers "name": "Balanced Testing" } """ - - @failing - Scenario: Set the default funding source - When I POST to /cards with the body: - """ - { - "number": "4111 1111 1111 1111", - "expiration_month": 12, - "expiration_year": 2016, - "cvv": "123", - "address": { - "line1": "965 Mission St", - "postal_code": "94103" - } - } - """ - Then I should get a 201 Created status code - And the response is valid according to the "cards" schema - - Given I have created a customer - When I make a PUT request to the link "href" with the body: - """ - { - "links": { - "source": "#{@card_id}", - } - } - """ - Then I should get a 200 OK status code - And the response is valid according to the "customers" schema - And the fields on this customer match: - """ - { - "links": { - "source": "#{@card_id}" - } - } - """ @failing Scenario: Set the default destination From e11eda824ff74f7463b15bff6ba64810d2666f3d Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 6 Jan 2014 23:04:34 +0000 Subject: [PATCH 084/154] Some movement on underwriting a customer See #468 for more. --- features/customers.feature | 2 +- lib/balanced/tiny_client.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/features/customers.feature b/features/customers.feature index 6912af5..e701c8e 100644 --- a/features/customers.feature +++ b/features/customers.feature @@ -35,7 +35,7 @@ Feature: Customers } """ - @failing + @failing @gh-468 Scenario: Underwrite a customer When I Post to /customers with the body: """ diff --git a/lib/balanced/tiny_client.rb b/lib/balanced/tiny_client.rb index 4ac12ad..1378da6 100644 --- a/lib/balanced/tiny_client.rb +++ b/lib/balanced/tiny_client.rb @@ -42,7 +42,7 @@ def post(endpoint, body, env={}) response end - def put(endpoint, body) + def put(endpoint, body, env={}) body = JSON.parse(body) if body.is_a? String options = { headers: { From 0536af76b479464af0c9131a8112a3924d3c32e9 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 7 Jan 2014 00:35:03 +0000 Subject: [PATCH 085/154] Moving forward on 'create an order' There's still a failure, unsure which part is right or wrong. --- features/orders.feature | 4 +- features/step_definitions/customers.rb | 10 ++++- fixtures/_models/order.json | 38 +++++++----------- fixtures/orders.json | 54 ++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 27 deletions(-) create mode 100644 fixtures/orders.json diff --git a/features/orders.feature b/features/orders.feature index a0c9cae..3010f1c 100644 --- a/features/orders.feature +++ b/features/orders.feature @@ -1,8 +1,8 @@ Feature: Orders - @failing + @failing @gh-469 Scenario: Create an order - Given I have an underwritten customer + Given I have created a customer When I make a POST request to /customers/:customer_id/orders Then I should get a 201 Created status code diff --git a/features/step_definitions/customers.rb b/features/step_definitions/customers.rb index 085f080..3992b6d 100644 --- a/features/step_definitions/customers.rb +++ b/features/step_definitions/customers.rb @@ -1,5 +1,13 @@ Given(/^I have created a customer$/) do - @client.post('/customers', {}) + @client.post('/customers', + { + name: 'Henry Ford', + dob: '1863-07', + address: { + postal_code: '48120' + } + } + ) @customer_id = @client['id'] @client.add_hydrate :customer_id, @customer_id diff --git a/fixtures/_models/order.json b/fixtures/_models/order.json index 04e6e26..e6d5f01 100644 --- a/fixtures/_models/order.json +++ b/fixtures/_models/order.json @@ -4,68 +4,58 @@ "properties": { "id": { "type": "string", - "pattern": "OR[a-zA-Z0-9]{16,32}", - "required": true + "pattern": "OR[a-zA-Z0-9]{16,32}" }, "href": { "type": "string", - "format": "uri", - "required": true + "format": "uri" }, "created_at": { "type": "string", - "format": "date-time", - "required": true + "format": "date-time" }, "updated_at": { "type": "string", - "format": "date-time", - "required": true + "format": "date-time" }, "currency": { "type": "string", "enum": [ "USD" - ], - "required": true + ] }, "amount": { "type": "integer", - "minimum": 0, - "required": true + "minimum": 0 }, "amount_escrowed": { "type": "integer", - "minimum": 0, - "required": true + "minimum": 0 }, "description": { "type": [ "string", "null" - ], - "required": true + ] }, "delivery_address": { - "$ref": "address.json", - "required": true + "$ref": "address.json" }, "meta": { - "type": "object", - "required": true + "type": "object" }, "links": { "type": "object", "properties": { "merchant": { "type": "string", - "pattern": "(CU|AC)[a-zA-Z0-9]{16,32}", - "required": true + "pattern": "(CU|AC)[a-zA-Z0-9]{16,32}" } }, - "required": true, + "required": [ "merchant" ], "additionalProperties": false } }, + "required": ["id", "href", "created_at", "updated_at", "currency", "amount", "amount_escrowed", "description", "delivery_address", "meta", "links"], "additionalProperties": false -} \ No newline at end of file +} diff --git a/fixtures/orders.json b/fixtures/orders.json new file mode 100644 index 0000000..cb37dbe --- /dev/null +++ b/fixtures/orders.json @@ -0,0 +1,54 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "links": { + "type": "object", + "properties": { + "orders.debits": { + "type": "string", + "format": "uri", + "pattern": "/orders/{orders.id}/debits" + }, + "orders.refunds": { + "type": "string", + "format": "uri", + "pattern": "/orders/{orders.id}/refunds" + }, + "orders.credits": { + "type": "string", + "format": "uri", + "pattern": "/orders/{orders.id}/credits" + }, + "orders.reversals": { + "type": "string", + "format": "uri", + "pattern": "/orders/{orders.id}/reversals" + }, + "orders.buyers": { + "type": "string", + "format": "uri", + "pattern": "/orders/{orders.id}/buyers" + }, + "orders.merchant": { + "type": "string", + "format": "uri", + "pattern": "/customers/{orders.merchant}" + } + }, + "required": ["orders.debits", "orders.refunds", "orders.credits", "orders.reversals", "orders.buyers", "orders.merchant"] + }, + "meta": { + "type": "object" + }, + "orders": { + "items": { + "$ref": "_models/order.json" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true + } + }, + "required": ["orders"] +} From a68ddd8c0fb94ea460f2698ee598218513fa1923 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 7 Jan 2014 01:22:03 +0000 Subject: [PATCH 086/154] progress on checking escrow on orders. Same delivery address issue --- features/orders.feature | 10 ++++++---- features/step_definitions/http_steps.rb | 8 +++++++- features/step_definitions/orders.rb | 4 ++++ lib/balanced/tiny_client.rb | 23 +++++++++++++++-------- 4 files changed, 32 insertions(+), 13 deletions(-) create mode 100644 features/step_definitions/orders.rb diff --git a/features/orders.feature b/features/orders.feature index 3010f1c..474083d 100644 --- a/features/orders.feature +++ b/features/orders.feature @@ -8,20 +8,22 @@ Feature: Orders Then I should get a 201 Created status code And the response is valid according to the "orders" schema - @failing + @failing @gh-469 Scenario: Checking escrow of order after creating a debit - Given I have tokenized a customer card + Given I have created an order + And I have tokenized a customer card And I make a POST request to the link "cards.debits" with the body: """ { - "order": "#{@orders_id}", + "order": "<%= @order_id %>", "amount": 1234 } """ - When I make a GET request to the link /orders/:order_id + When I make a GET request to /orders/:order_id Then I should get a 200 OK status code And the response is valid according to the "orders" schema + And the fields on this order match: """ { "amount_escrowed": 1234 diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 3042977..c22d14f 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -1,5 +1,9 @@ +require 'erb' + When(/^I (\w+) to (\/\S*?)$/) do |verb, url| - @client.verb(verb, @client.hydrater(url)) + @client.verb(verb, @client.hydrater(url), env) + @order_id = @client['orders']['id'] rescue nil + @client.add_hydrate(:order_id, @order_id) if @order_id end When(/^I (\w+) to (\/\S*?) with the body:$/) do |verb, url, body| @@ -17,6 +21,7 @@ def env "cards_id" => @card_id, "debits_id" => @debit_id, "customers_id" => @customer_id, + "orders_id" => @order_id, } end @@ -27,6 +32,7 @@ def env end When(/^I make a (\w+) request to the link "(.*?)" with the body:$/) do |verb, keys, body| + body = ERB.new(body).result(binding) body = @client.send(verb.downcase, @client.hydrater(@client.last_body["links"][keys]), JSON.parse(body), env) @credit_id = @client['credits']['id'] rescue nil @cards_id = @client['cards']['id'] rescue nil diff --git a/features/step_definitions/orders.rb b/features/step_definitions/orders.rb new file mode 100644 index 0000000..22f1c83 --- /dev/null +++ b/features/step_definitions/orders.rb @@ -0,0 +1,4 @@ +Given(/^I have created an order$/) do + step 'I have created a customer' + step 'I make a POST request to /customers/:customer_id/orders' +end diff --git a/lib/balanced/tiny_client.rb b/lib/balanced/tiny_client.rb index 1378da6..d4e5682 100644 --- a/lib/balanced/tiny_client.rb +++ b/lib/balanced/tiny_client.rb @@ -28,6 +28,14 @@ def post(endpoint, body, env={}) } } + url = expand_url(endpoint, env) + + response = HTTParty.post(url, options) + @responses << response + response + end + + def expand_url(endpoint, env) require 'uri_template' # I am so sorry template = "#{@root_url}#{endpoint}".gsub(/{(.*?)}/) do @@ -35,11 +43,7 @@ def post(endpoint, body, env={}) end # TODO: does using . in uri variables make sense? http://tools.ietf.org/html/rfc6570#section-3.2.1 template = URITemplate.new(template) - url = template.expand(env) - - response = HTTParty.post(url, options) - @responses << response - response + template.expand(env) end def put(endpoint, body, env={}) @@ -79,8 +83,8 @@ def patch(endpoint, body) response end - def get(endpoint) - verb 'GET', endpoint + def get(endpoint, body=nil, env={}) + verb 'GET', endpoint, env end def delete(endpoint, body=nil, env={}) @@ -101,7 +105,10 @@ def verb(verb, url, env={}) password: '', } } - response = HTTParty.send(verb.downcase, "#{$root_url}#{url}", options) + + url = expand_url(url, env) + + response = HTTParty.send(verb.downcase, url, options) @responses << response response end From f7a6c77a8f6ab549c4d0598e5afc9ed317e25956 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 7 Jan 2014 19:05:33 +0000 Subject: [PATCH 087/154] Add in debugging steps. Previously, @matthewfl had added some logging stuff, this is a bit heavier, but often useful. --- features/step_definitions/debugging.rb | 13 +++++++++++++ features/step_definitions/http_steps.rb | 7 ++----- 2 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 features/step_definitions/debugging.rb diff --git a/features/step_definitions/debugging.rb b/features/step_definitions/debugging.rb new file mode 100644 index 0000000..fd1a0ea --- /dev/null +++ b/features/step_definitions/debugging.rb @@ -0,0 +1,13 @@ +Then(/^debug$/) do + puts "HTTP status code: #{@client.last_code}" + puts "HTTP body: #{@client.last_body}" +end + +require 'logger' +$logger = Logger.new(STDOUT) +$logger.level = Logger::FATAL + +When(/^logging is enabled$/) do + $logger.level = Logger::DEBUG +end + diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index c22d14f..ba38cfc 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -1,6 +1,8 @@ require 'erb' When(/^I (\w+) to (\/\S*?)$/) do |verb, url| + $logger.debug("Making request to #{url}") + $logger.debug("hydrated: #{@client.hydrater(url)}") @client.verb(verb, @client.hydrater(url), env) @order_id = @client['orders']['id'] rescue nil @client.add_hydrate(:order_id, @order_id) if @order_id @@ -132,11 +134,6 @@ def checker(from, of, nesting) end -Then(/^debug$/) do - puts "HTTP status code: #{@client.last_code}" - puts "HTTP body: #{@client.last_body}" -end - # TODO: move? Before do |scenario| From fba62850c74eed80d1c87772882198944394d497 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 7 Jan 2014 19:06:07 +0000 Subject: [PATCH 088/154] Making some progress on 'checking escrow of order' Erroring for the same reason the other Orders are. --- features/orders.feature | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/features/orders.feature b/features/orders.feature index 474083d..2c9f98b 100644 --- a/features/orders.feature +++ b/features/orders.feature @@ -30,20 +30,22 @@ Feature: Orders } """ - @failing + @failing @gh-469 Scenario: Checking escrow of order after creating a credit - Given I have sufficient funds in my marketplace + Given I have created an order + And I have sufficient funds in my marketplace And I have tokenized a bank account And I POST to /bank_accounts/:bank_account_id/credits with the JSON API body: """ { - "order": "#{@orders_id}", + "order": "<%= @orders_id %>", "amount": 1234 } """ - When I make a GET request to the link /orders/:order_id + When I make a GET request to /orders/:order_id Then I should get a 200 OK status code And the response is valid according to the "orders" schema + And the fields on this order match: """ { "amount_escrowed": 0 From 5ee8012d784eafc026c2199e447f126ef0eadd5c Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 7 Jan 2014 19:14:52 +0000 Subject: [PATCH 089/154] Making progress on 'orders can't be credited more... ... than escrow balance. --- features/orders.feature | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/features/orders.feature b/features/orders.feature index 2c9f98b..2655b1e 100644 --- a/features/orders.feature +++ b/features/orders.feature @@ -53,19 +53,21 @@ Feature: Orders """ - @failing + @failing @gh-469 Scenario: Orders cannot be credited more than escrow balance - Given I have tokenized a customer card + Given I have created an order + And I have tokenized a customer card And I make a POST request to the link "cards.debits" with the body: """ { - "order": "#{@orders_id}", + "order": "<%= @orders_id $>", "amount": 1234 } """ - When I make a GET request to the link /orders/:order_id + When I make a GET request to /orders/:order_id Then I should get a 200 OK status code And the response is valid according to the "orders" schema + And the fields on this order match: """ { "amount_escrowed": 1234 From 5d33e555bc296ab560af089068b70d25c307ab62 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 7 Jan 2014 19:22:27 +0000 Subject: [PATCH 090/154] Making progress on 'Create a refund' --- features/orders.feature | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/features/orders.feature b/features/orders.feature index 2655b1e..fab0a51 100644 --- a/features/orders.feature +++ b/features/orders.feature @@ -89,15 +89,17 @@ Feature: Orders } """ - @failing + @failing @gh-469 Scenario: Create a refund - Given I have tokenized a customer card + Given I have created an order + And I have tokenized a customer card And I have debited that card When I make a POST request to the link "debits.refunds" - And I make a GET request to the link /orders/:order_id + And I make a GET request to /orders/:order_id Then I should get a 200 OK status code - And the response is valid according to the "order" schema + And the response is valid according to the "orders" schema + And the fields on this error match: """ { "amount_escrowed": 0 From 3e1b0b82cdd1fd981e459fd90d620ba88c80cabc Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 7 Jan 2014 19:23:17 +0000 Subject: [PATCH 091/154] Update interpolation syntax I missed this one because the scenario fails before it hit this part. But it's pretty obvious it needs fixed. --- features/orders.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/orders.feature b/features/orders.feature index fab0a51..1548471 100644 --- a/features/orders.feature +++ b/features/orders.feature @@ -76,7 +76,7 @@ Feature: Orders And I POST to /bank_accounts/:bank_account_id/credits with the JSON API body: """ { - "order": "#{@orders_id}", + "order": "<%= @orders_id %>", "amount": 2000 } """ From 870fcdba6c2ec53f85823689267bfa18a5674fa8 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 7 Jan 2014 19:40:03 +0000 Subject: [PATCH 092/154] Typo fix: should be an order not an error The scenario failed before this, but I want to catch it now. Bady copy/paste on my part --- features/orders.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/orders.feature b/features/orders.feature index 1548471..7418767 100644 --- a/features/orders.feature +++ b/features/orders.feature @@ -99,7 +99,7 @@ Feature: Orders And I make a GET request to /orders/:order_id Then I should get a 200 OK status code And the response is valid according to the "orders" schema - And the fields on this error match: + And the fields on this order match: """ { "amount_escrowed": 0 From 51c6a76357cd8f9d3ca711f96c72a9ca55cc8115 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 7 Jan 2014 19:40:43 +0000 Subject: [PATCH 093/154] Making progress on create a reversal --- features/orders.feature | 18 ++++++++++++------ features/step_definitions/http_steps.rb | 6 +++++- lib/balanced/tiny_client.rb | 9 +++++++-- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/features/orders.feature b/features/orders.feature index 7418767..f1a2176 100644 --- a/features/orders.feature +++ b/features/orders.feature @@ -107,28 +107,34 @@ Feature: Orders """ - @failing + @failing @gh-469 Scenario: Create a reversal - Given I have tokenized a customer card - And I make a POST request to /customers/:customer_id/orders with the body + Given I have created an order + And I have tokenized a customer card + And I make a POST request to /customers/:customer_id/orders with the body: """ - "order": "#{@orders_id}", + { + "order": "<%= @orders_id %>", "amount": 1234 + } """ And I have tokenized a bank account And I make a POST request to the link "bank_accounts.credits" with the body: """ - "order": "#{@orders_id}", + { + "order": "<%= @orders_id %>", "amount": 1234 + } """ When I make a POST request to the link "credits.reversals" Then I should get a 201 Created status code And the response is valid according to the "reversals" schema - When I make a GET request to the link /orders/:order_id + When I make a GET request to /orders/:order_id Then I should get a 200 OK status code And the response is valid according to the "orders" schema + And the fields on this order match: """ { "amount_escrowed": 0 diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index ba38cfc..8411651 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -9,13 +9,17 @@ end When(/^I (\w+) to (\/\S*?) with the body:$/) do |verb, url, body| - @client.verb(verb, @client.hydrater(url), body) + @client.verb(verb, @client.hydrater(url), env, body) end When(/^I make a (\w+) request to (\/\S*?)$/) do |verb, url| step "I #{verb} to #{url}" end +When(/^I make a (\w+) request to (\/\S*?) with the body:$/) do |verb, url, body| + step "I #{verb} to #{url} with the body:", body +end + def env { "bank_accounts_id" => @bank_account_id, diff --git a/lib/balanced/tiny_client.rb b/lib/balanced/tiny_client.rb index d4e5682..b57a411 100644 --- a/lib/balanced/tiny_client.rb +++ b/lib/balanced/tiny_client.rb @@ -41,9 +41,12 @@ def expand_url(endpoint, env) template = "#{@root_url}#{endpoint}".gsub(/{(.*?)}/) do "{#{$1.gsub(".", "_")}}" end + $logger.debug("expanding: #{template} with env: #{env}") # TODO: does using . in uri variables make sense? http://tools.ietf.org/html/rfc6570#section-3.2.1 template = URITemplate.new(template) - template.expand(env) + url = template.expand(env) + $logger.debug("expanded: #{url}") + url end def put(endpoint, body, env={}) @@ -95,7 +98,7 @@ def add_response(response) @responses << response end - def verb(verb, url, env={}) + def verb(verb, url, env={}, body=nil) options = { headers: { 'Accept' => @accept_header @@ -106,6 +109,8 @@ def verb(verb, url, env={}) } } + options[:body] = body if body + url = expand_url(url, env) response = HTTParty.send(verb.downcase, url, options) From 37043f48d4495d044d049ae5048173437b448a03 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 7 Jan 2014 20:17:07 +0000 Subject: [PATCH 094/154] fix interpolation syntax let's just do this all at once --- features/orders.feature | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/features/orders.feature b/features/orders.feature index f1a2176..05efa67 100644 --- a/features/orders.feature +++ b/features/orders.feature @@ -148,15 +148,17 @@ Feature: Orders When I make a POST request to the link "cards.debits" with the body: """ { - "order": "#{@orders_id}", + "order": "<%= @orders_id %>", "amount": 1234 } """ And I have tokenized a bank account Then I make a POST request to the link "bank_accounts.credits" with the body: """ - "order": "#{@orders_id}", + { + "order": "<%= @orders_id %>", "amount": 1234 + } """ When I make a POST request to the link "debits.refunds" Then I should get a 409 status code @@ -190,7 +192,7 @@ Feature: Orders """ { "amount": 10000, - "order": "#{@orders_id}", + "order": "<%= @orders_id %>", } """ @@ -206,7 +208,7 @@ Feature: Orders """ { "amount": 10000, - "order": "#{@orders_id}", + "order": "<%= @orders_id %>", } """ Then I should get a 201 Created status code @@ -225,7 +227,7 @@ Feature: Orders And I make a POST request to the link "cards.debits" with the body: """ { - "order": "#{@orders_id}", + "order": "<%= @orders_id %>", "amount": 1234 } """ @@ -235,7 +237,7 @@ Feature: Orders """ { "amount": 1234, - "order": "#{@orders_id}", + "order": "<%= @orders_id %>", } """ Then I should get a 409 status code From 4ce588aa8d283ab890eb5a69ef583a3e775df5b2 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Tue, 7 Jan 2014 15:44:33 -0800 Subject: [PATCH 095/154] baby steps --- features/customers.feature | 23 ++++++++++++----------- features/step_definitions/http_steps.rb | 4 ++++ features/support/initial_setup.rb | 2 +- lib/balanced/tiny_client.rb | 4 +++- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/features/customers.feature b/features/customers.feature index e701c8e..a696260 100644 --- a/features/customers.feature +++ b/features/customers.feature @@ -16,22 +16,23 @@ Feature: Customers } """ - @failing Scenario: Set the default destination - When I make a PATCH request to the link "href" with the body: + Given I have created a customer without a card and bank account + And I have tokenized a bank account + When I make a PATCH request to /customers/:customer_id with the body: """ - { - "op": "replace", - "path": "/customers/0/links/destination", - "value": "#{@bank_accounts_id}" - } + [{ + "op": "replace", + "path": "/customers/0/links/destination", + "value": ":bank_account_id" + }] """ Then I should get a 200 OK status code And the response is valid according to the "customers" schema And the fields on this customer match: """ { - "links": { "destination": "#{@bank_accounts_id}" } + "links": { "destination": ":bank_account_id" } } """ @@ -48,11 +49,11 @@ Feature: Customers And the fields on this customer match: """ { - "merchant_status": "need-more-information" + "merchant_status": "no-match" } """ - When I make a PUT request to the link "href" with the body: + When I PUT to /customers/:customer_id with the body: """ { "dob_month": 7, @@ -62,6 +63,7 @@ Feature: Customers } } """ + Then debug Then I should get a 200 OK status code And the response is valid according to the "customers" schema And the fields on this customer match: @@ -70,4 +72,3 @@ Feature: Customers "merchant_status": "underwritten" } """ - diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 8411651..2d659be 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -9,6 +9,10 @@ end When(/^I (\w+) to (\/\S*?) with the body:$/) do |verb, url, body| + $logger.debug("Making request to #{url}") + $logger.debug("hydrated: #{@client.hydrater(url)}") + body = @client.hydrater body + $logger.debug("body: #{body}") @client.verb(verb, @client.hydrater(url), env, body) end diff --git a/features/support/initial_setup.rb b/features/support/initial_setup.rb index 16184c5..4c33994 100644 --- a/features/support/initial_setup.rb +++ b/features/support/initial_setup.rb @@ -2,7 +2,7 @@ require 'json' # TODO: move this into the lib -$root_url = 'https://api.balancedpayments.com' +$root_url = ENV['BALANCED_ROOT'] || 'https://api.balancedpayments.com' $accept_header = 'application/vnd.api+json;revision=1.1' # First, we need to create an API key. This is as easy as making a POST request. diff --git a/lib/balanced/tiny_client.rb b/lib/balanced/tiny_client.rb index b57a411..93b106a 100644 --- a/lib/balanced/tiny_client.rb +++ b/lib/balanced/tiny_client.rb @@ -101,8 +101,10 @@ def add_response(response) def verb(verb, url, env={}, body=nil) options = { headers: { - 'Accept' => @accept_header + 'Accept' => @accept_header, + 'Content-Type' => "application/json", # github: https://github.com/balanced/balanced-api/issues/458 }, + body: JSON.dump(body), # sometimes we send arrays and then it gets confused basic_auth: { username: @api_secret, password: '', From 6083a08e122127b4bce93b58894bcefd52809c83 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 7 Jan 2014 23:45:18 +0000 Subject: [PATCH 096/154] New feature: underwrite a customer Closes #468 --- features/customers.feature | 8 ++++---- features/step_definitions/http_steps.rb | 7 +++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/features/customers.feature b/features/customers.feature index e701c8e..499a18f 100644 --- a/features/customers.feature +++ b/features/customers.feature @@ -35,9 +35,8 @@ Feature: Customers } """ - @failing @gh-468 Scenario: Underwrite a customer - When I Post to /customers with the body: + When I POST to /customers with the body: """ { "name": "Henry Ford" @@ -48,13 +47,14 @@ Feature: Customers And the fields on this customer match: """ { - "merchant_status": "need-more-information" + "merchant_status": "no-match" } """ - When I make a PUT request to the link "href" with the body: + When I make a PUT request to the href "href" with the body: """ { + "name": "Henry Ford", "dob_month": 7, "dob_year": 1963, "address": { diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 8411651..b4b7877 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -10,6 +10,8 @@ When(/^I (\w+) to (\/\S*?) with the body:$/) do |verb, url, body| @client.verb(verb, @client.hydrater(url), env, body) + @customer_id = @client['customers']['id'] rescue nil + @client.add_hydrate(:customer_id, @customer_id) if @customer_id end When(/^I make a (\w+) request to (\/\S*?)$/) do |verb, url| @@ -37,6 +39,11 @@ def env @client.send(verb.downcase, link, {}, env) end +When(/^I make a (\w+) request to the href "(.*?)" with the body:$/) do |verb, keys, body| + link = @client[keys] || @client.inject(keys) + @client.send(verb.downcase, link, body, env) +end + When(/^I make a (\w+) request to the link "(.*?)" with the body:$/) do |verb, keys, body| body = ERB.new(body).result(binding) body = @client.send(verb.downcase, @client.hydrater(@client.last_body["links"][keys]), JSON.parse(body), env) From 585f67fc4ac9149a09982e50baeab0a70e9b6d22 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Wed, 8 Jan 2014 00:15:25 +0000 Subject: [PATCH 097/154] making some movement on #471 --- features/orders.feature | 8 +++++--- features/step_definitions/bank_accounts.rb | 1 + features/step_definitions/http_steps.rb | 8 ++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/features/orders.feature b/features/orders.feature index 05efa67..cd52f83 100644 --- a/features/orders.feature +++ b/features/orders.feature @@ -142,9 +142,10 @@ Feature: Orders """ - @failing + @failing @gh-471 Scenario: Create a failed refund when insufficient funds are in order escrow - Given I have tokenized a card + Given I have created an order + And I have tokenized a card When I make a POST request to the link "cards.debits" with the body: """ { @@ -160,7 +161,8 @@ Feature: Orders "amount": 1234 } """ - When I make a POST request to the link "debits.refunds" + + When I make a POST request to the link "debits.refunds" of that debit Then I should get a 409 status code And the response is valid according to the "errors" schema And the fields on this error match: diff --git a/features/step_definitions/bank_accounts.rb b/features/step_definitions/bank_accounts.rb index e3350fd..a6ab36e 100644 --- a/features/step_definitions/bank_accounts.rb +++ b/features/step_definitions/bank_accounts.rb @@ -7,6 +7,7 @@ }) @bank_account_id = @client['bank_accounts']['id'] @client.add_hydrate(:bank_account_id, @bank_account_id) + @client.add_hydrate(:bank_accounts_id, @bank_account_id) end diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 4c72804..9af7cc4 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -50,6 +50,7 @@ def env When(/^I make a (\w+) request to the link "(.*?)" with the body:$/) do |verb, keys, body| body = ERB.new(body).result(binding) + $logger.debug("Requesting hydrated: #{@client.hydrater(@client.last_body["links"][keys])}") body = @client.send(verb.downcase, @client.hydrater(@client.last_body["links"][keys]), JSON.parse(body), env) @credit_id = @client['credits']['id'] rescue nil @cards_id = @client['cards']['id'] rescue nil @@ -57,7 +58,14 @@ def env body end +When(/^I make a (\w+) request to the link "(.*?)" of that (\w+)$/) do |verb, keys, resource| + id = instance_variable_get("@#{resource}_id") + @client.get("/resources/#{id}") + step %Q{I make a #{verb} request to the link "#{keys}"} +end + When(/^I make a (\w+) request to the link "(.*?)"$/) do |verb, keys| + $logger.debug("Requesting hydrated: #{@client.hydrater(@client.last_body["links"][keys])}") body = @client.send(verb.downcase, @client.hydrater(@client.last_body["links"][keys]), {}, env) @credit_id = @client['credits']['id'] rescue nil body From 48ec87415546b8853442e88e77759d59d8b66774 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Sat, 11 Jan 2014 21:18:32 +0000 Subject: [PATCH 098/154] Make progress on 'transactions inherit descriptions' --- features/orders.feature | 19 ++++++++++++------- features/step_definitions/http_steps.rb | 5 +++++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/features/orders.feature b/features/orders.feature index cd52f83..d343ee9 100644 --- a/features/orders.feature +++ b/features/orders.feature @@ -172,21 +172,24 @@ Feature: Orders } """ - @failing + @failing @gh-469 Scenario: Transactions should inherit the description of the order by default - Given I have tokenized a bank account - When I make a POST request to the link "customers.orders" with the body: + Given I have created an order + And I have tokenized a bank account + When I fetch the customer + And I make a POST request to the link "customers.orders" with the body: """ { - "description": "Beats by Dr. Dre" } + "description": "Beats by Dr. Dre" } """ Then I should get a 201 Created status code And the response is valid according to the "orders" schema + And the fields on this order match: """ { - { "description": "Beats by Dr. Dre" } + "description": "Beats by Dr. Dre" } """ @@ -200,9 +203,10 @@ Feature: Orders Then I should get a 201 Created status code And the response is valid according to the "debits" schema + And the fields on this debit match: """ { - "description": "Beats by Dr. Dre" } + "description": "Beats by Dr. Dre" } """ @@ -214,7 +218,8 @@ Feature: Orders } """ Then I should get a 201 Created status code - And the response is valid according to the "debits" schema + And the response is valid according to the "credits" schema + And the fields on this credit match: """ { { "description": "Beats by Dr. Dre"} diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 9af7cc4..c853a98 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -64,6 +64,11 @@ def env step %Q{I make a #{verb} request to the link "#{keys}"} end +When(/^I fetch the (\w+)$/) do |resource| + id = instance_variable_get("@#{resource}_id") + @client.get("/resources/#{id}") +end + When(/^I make a (\w+) request to the link "(.*?)"$/) do |verb, keys| $logger.debug("Requesting hydrated: #{@client.hydrater(@client.last_body["links"][keys])}") body = @client.send(verb.downcase, @client.hydrater(@client.last_body["links"][keys]), {}, env) From 48b623977b90373584d6e385a6e414cde6cfe455 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Sat, 11 Jan 2014 21:40:59 +0000 Subject: [PATCH 099/154] make some progress on crediting an unverified merchant. #474 is tracking this particular failure. --- features/orders.feature | 10 +++-- features/step_definitions/bank_accounts.rb | 52 ++++++++++++++++++++++ features/step_definitions/http_steps.rb | 5 ++- 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/features/orders.feature b/features/orders.feature index d343ee9..82e6c88 100644 --- a/features/orders.feature +++ b/features/orders.feature @@ -226,11 +226,12 @@ Feature: Orders } """ - @failing + @failing @gh-474 Scenario: Crediting an unverified merchant will result in failure - Given I have an unverified customer with a tokenized card - And I make a POST request to 'customers.orders' + Given I have created a non-underwritten customer with a tokenized bank account + And I make a POST request to the link "customers.orders" + And I fetch the card And I make a POST request to the link "cards.debits" with the body: """ { @@ -240,11 +241,12 @@ Feature: Orders """ Then I should get a 201 Created status code + When I fetch the bank account And I make a POST request to the link "bank_accounts.credits" with the body: """ { "amount": 1234, - "order": "<%= @orders_id %>", + "order": "<%= @orders_id %>" } """ Then I should get a 409 status code diff --git a/features/step_definitions/bank_accounts.rb b/features/step_definitions/bank_accounts.rb index a6ab36e..2d80251 100644 --- a/features/step_definitions/bank_accounts.rb +++ b/features/step_definitions/bank_accounts.rb @@ -56,3 +56,55 @@ } }) end + +Given(/^I have created a non-underwritten customer with a tokenized bank account$/) do + @client.post('/customers', {}) + @customer_id = @client['id'] + @client.add_hydrate :customer_id, @customer_id + + @customer_url = @client['customers']['href'] + + # tokenize a card for them + @client.post('/cards', + { + number: "4111 1111 1111 1111", + expiration_month: 12, + expiration_year: 2016, + cvv: "123", + address: { + line1: "965 Mission St", + postal_code: "94103" + } + } + ) + card_url = @client['cards']['href'] + @card_id = @client['cards']['id'] + @client.patch(card_url, + [{ + op: "replace", + path: "/cards/0/links/customer", + value: @customer_id, + }] + ) + + # associate their card so that they have a funding source + @client.patch(@customer_url, + [{ + op: "replace", + path: "/customers/0/links/source", + value: @card_id + }] + ) + + step 'I have tokenized a bank account' + @client.put("/bank_accounts/#{@bank_account_id}", { + links: { + customer: @customer_id + } + }) + + ## TODO: fix hax + # Right now, we rely on last_body in places becuase the client is mutable + # this is bad and we should stop it. + @client.get(@customer_url) +end diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index c853a98..f7ff5ae 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -49,6 +49,7 @@ def env end When(/^I make a (\w+) request to the link "(.*?)" with the body:$/) do |verb, keys, body| + puts @client.last_body body = ERB.new(body).result(binding) $logger.debug("Requesting hydrated: #{@client.hydrater(@client.last_body["links"][keys])}") body = @client.send(verb.downcase, @client.hydrater(@client.last_body["links"][keys]), JSON.parse(body), env) @@ -64,8 +65,10 @@ def env step %Q{I make a #{verb} request to the link "#{keys}"} end -When(/^I fetch the (\w+)$/) do |resource| +When(/^I fetch the (.*+)$/) do |resource| + resource = resource.gsub(/\s/, "_") id = instance_variable_get("@#{resource}_id") + puts id @client.get("/resources/#{id}") end From 413775d120663caad82306e9d933f5b513c26596 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 13 Jan 2014 22:18:06 +0000 Subject: [PATCH 100/154] Making progress on 'cancelling an order' --- features/checkout_flow.feature | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/features/checkout_flow.feature b/features/checkout_flow.feature index b51fede..e3f449f 100644 --- a/features/checkout_flow.feature +++ b/features/checkout_flow.feature @@ -1,20 +1,22 @@ Feature: Credit cards - @failing + @failing @gh-469 Scenario: Canceling an order - Given I have an order - Then I make a GET request to the link /orders/:order_id + Given I have created an order + Then I make a GET request to /orders/:order_id Then I should get a 200 OK status code And the response is valid according to the "orders" schema + And the fields on this order match: """ { "description": "Catherine Malandrino Black Top" } """ - Then I make a GET request to "orders.debits" + Then I make a GET request to the link "orders.debits" Then I should get a 200 OK status code And the response is valid according to the "debits" schema + And the fields on this debit match: """ { "amount": 10000, @@ -22,9 +24,10 @@ Feature: Credit cards } """ - Then I make a POST request to "debits.refunds" + Then I make a POST request to the link "debits.refunds" Then I should get a 201 OK status code And the response is valid according to the "refunds" schema + And the fields on this refund match: """ { "amount": 10000, @@ -32,9 +35,10 @@ Feature: Credit cards } """ - Then I make a GET request to the link /orders/:order_id + Then I make a GET request to /orders/:order_id Then I should get a 200 OK status code And the response is valid according to the "orders" schema + And the fields on this order match: """ { "amount": 0, From 0afcceb7acd28c2e22873ac496639e8471e78891 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 13 Jan 2014 23:10:38 +0000 Subject: [PATCH 101/154] Making progress on 'existing buyer makes a purchase with a new card' #476 has the report --- features/checkout_flow.feature | 31 +++++++++++++++---------- features/step_definitions/http_steps.rb | 11 +++++++++ 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/features/checkout_flow.feature b/features/checkout_flow.feature index e3f449f..a0ce4ad 100644 --- a/features/checkout_flow.feature +++ b/features/checkout_flow.feature @@ -48,17 +48,19 @@ Feature: Credit cards } """ - @failing + @failing @gh-476 Scenario: Existing buyer makes a purchase with a new card + Given logging is enabled + Given I have created a customer When I make a GET request to /customers/:customer_id Then I should get a 200 OK status code And the response is valid according to the "customers" schema - Then I make a GET request to "customers.source" + Then I make a GET request to the link "customers.source" Then I should get a 200 OK status code And the response is valid according to the "cards" schema - Then I make a POST request to /cards with the JSON API body: + Then I POST to /cards with the JSON API body: """ { "name": "Darius the Great", @@ -81,6 +83,7 @@ Feature: Credit cards When I make a GET request to /cards/:cards_id Then I should get a 200 OK status code And the response is valid according to the "cards" schema + And the fields on this card match: """ { "avs_street_match": "yes", @@ -89,23 +92,24 @@ Feature: Credit cards } """ - When I make a PATCH request to /customers/:customer_id with the JSON API body: + When I PATCH to /customers/:customer_id with the JSON API body: """ { "op": "replace", "path": "/customers/0/links/source", - "value": "#{@cards_id}" + "value": "<%= @cards_id %>" } """ Then I should get a 200 OK status code And the response is valid according to the "customers" schema + And the fields on this customer match: """ { - "links": { "source": "#{@cards_id}" } + "links": { "source": "<%= @cards_id %>" } } """ - When I make a POST request to /customers/:customer_id/orders with the body + When I make a POST request to /customers/:customer_id/orders with the body: """ { "description": "Catherine Malandrino Black Top", @@ -123,34 +127,36 @@ Feature: Credit cards """ Then I should get a 201 CREATED status code And the response is valid according to the "orders" schema + And the fields on this order match: """ { - "links":{ "merchant": "#{@customers_id}" } + "links":{ "merchant": "<%= @customers_id %>" } } """ - When I make a POST request to "customers.debits" with the body + When I make a POST request to the link "customers.debits" with the body: """ { "amount": 10000, - "order": "#{@orders_id}", + "order": "<%= @orders_id %>", "appears_on_statement_as": "Vaunte-Alice Ryan" } """ Then I should get a 201 CREATED status code And the response is valid according to the "debits" schema + And the fields on this debit match: """ { "description": "Catherine Malandrino Black Top", "appears_on_statement_as": "BAL*Vaunte-Alice Ryan", "status": "succeeded", "links": { - "order": "#{@orders_id}", + "order": "<%= @orders_id %>", } } """ - When I make a PUT request to /orders/:order_id with the JSON API body: + When I PUT to /orders/:order_id with the JSON API body: """ { "meta": { @@ -162,6 +168,7 @@ Feature: Credit cards """ Then I should get a 200 OK status code And the response is valid according to the "orders" schema + And the fields on this order match: """ { "meta": { diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index f7ff5ae..97acf56 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -6,6 +6,8 @@ @client.verb(verb, @client.hydrater(url), env) @order_id = @client['orders']['id'] rescue nil @client.add_hydrate(:order_id, @order_id) if @order_id + @customer_source = @client['customers']['links']['source'] rescue nil + @client.add_hydrate(:customers_source, @customer_source) if @customer_source end When(/^I (\w+) to (\/\S*?) with the body:$/) do |verb, url, body| @@ -33,6 +35,7 @@ def env "cards_id" => @card_id, "debits_id" => @debit_id, "customers_id" => @customer_id, + "customers_source" => @customer_source, "orders_id" => @order_id, } end @@ -55,6 +58,7 @@ def env body = @client.send(verb.downcase, @client.hydrater(@client.last_body["links"][keys]), JSON.parse(body), env) @credit_id = @client['credits']['id'] rescue nil @cards_id = @client['cards']['id'] rescue nil + @client.add_hydrate(:cards_id, @cards_id) if @cards_id @debit_url = @client['debits']['href'] rescue nil body end @@ -75,6 +79,8 @@ def env When(/^I make a (\w+) request to the link "(.*?)"$/) do |verb, keys| $logger.debug("Requesting hydrated: #{@client.hydrater(@client.last_body["links"][keys])}") body = @client.send(verb.downcase, @client.hydrater(@client.last_body["links"][keys]), {}, env) + @cards_id = @client['cards']['id'] rescue nil + @client.add_hydrate(:cards_id, @cards_id) if @cards_id @credit_id = @client['credits']['id'] rescue nil body end @@ -111,6 +117,7 @@ def env When(/^I POST to (\/\S*) with the JSON API body:$/) do |url, body| body = @client.post(@client.hydrater(url), body) @credit_id = @client['credits']['id'] rescue nil + @card_id = @client['cards']['id'] rescue nil body end @@ -118,6 +125,10 @@ def env @client.put(@client.hydrater(url), @client.hydrater(body)) end +When(/^I PATCH to (\/\S*) with the JSON API body:$/) do |url, body| + @client.patch(@client.hydrater(url), body) +end + require 'json-schema' Then(/^the response has this schema:$/) do |schema| @client.validate(schema) From 37eb86b6b995fd3ab4c798ad8fb225beaa8bf22c Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Mon, 13 Jan 2014 15:12:07 -0800 Subject: [PATCH 102/154] some cleaning --- features/checkout_flow.feature | 20 +++++----- features/credits.feature | 2 + features/orders.feature | 49 +++++++++++++------------ features/rest/bank_accounts.feature | 5 --- features/rest/cards.feature | 9 +---- features/rest/customers.feature | 2 - features/step_definitions/debugging.rb | 2 +- features/step_definitions/http_steps.rb | 11 +++--- features/step_definitions/orders.rb | 7 ++-- lib/balanced/tiny_client.rb | 1 + 10 files changed, 52 insertions(+), 56 deletions(-) diff --git a/features/checkout_flow.feature b/features/checkout_flow.feature index a0ce4ad..b9ee7b6 100644 --- a/features/checkout_flow.feature +++ b/features/checkout_flow.feature @@ -6,6 +6,8 @@ Feature: Credit cards Then I make a GET request to /orders/:order_id Then I should get a 200 OK status code And the response is valid according to the "orders" schema + # how is this going to match, as we have not set any values on the order + # so the description is going to be empty And the fields on this order match: """ { @@ -48,9 +50,8 @@ Feature: Credit cards } """ - @failing @gh-476 + @failing @gh-476 @focus Scenario: Existing buyer makes a purchase with a new card - Given logging is enabled Given I have created a customer When I make a GET request to /customers/:customer_id Then I should get a 200 OK status code @@ -80,7 +81,7 @@ Feature: Credit cards Then I should get a 201 CREATED status code And the response is valid according to the "cards" schema - When I make a GET request to /cards/:cards_id + When I make a GET request to /cards/:card_id Then I should get a 200 OK status code And the response is valid according to the "cards" schema And the fields on this card match: @@ -92,20 +93,21 @@ Feature: Credit cards } """ + And debug When I PATCH to /customers/:customer_id with the JSON API body: """ - { + [{ "op": "replace", "path": "/customers/0/links/source", - "value": "<%= @cards_id %>" - } + "value": ":card_id" + }] """ Then I should get a 200 OK status code And the response is valid according to the "customers" schema And the fields on this customer match: """ { - "links": { "source": "<%= @cards_id %>" } + "links": { "source": ":card_id" } } """ @@ -130,7 +132,7 @@ Feature: Credit cards And the fields on this order match: """ { - "links":{ "merchant": "<%= @customers_id %>" } + "links":{ "merchant": ":customer_id" } } """ @@ -138,7 +140,7 @@ Feature: Credit cards """ { "amount": 10000, - "order": "<%= @orders_id %>", + "order": ":order_id", "appears_on_statement_as": "Vaunte-Alice Ryan" } """ diff --git a/features/credits.feature b/features/credits.feature index 0df2486..4db4f13 100644 --- a/features/credits.feature +++ b/features/credits.feature @@ -10,6 +10,8 @@ Feature: Credits When I make a POST request to /cards/:cards_id/credits Then I should get a 404 status code + # the 404 error comes back in the error schema from rev0 instead of rev1 + And the response is valid according to the "errors" schema And the fields on this error match: """ diff --git a/features/orders.feature b/features/orders.feature index 82e6c88..f9ac7a0 100644 --- a/features/orders.feature +++ b/features/orders.feature @@ -1,6 +1,5 @@ Feature: Orders - @failing @gh-469 Scenario: Create an order Given I have created a customer When I make a POST request to /customers/:customer_id/orders @@ -8,14 +7,13 @@ Feature: Orders Then I should get a 201 Created status code And the response is valid according to the "orders" schema - @failing @gh-469 Scenario: Checking escrow of order after creating a debit Given I have created an order And I have tokenized a customer card And I make a POST request to the link "cards.debits" with the body: """ { - "order": "<%= @order_id %>", + "order": ":order_id", "amount": 1234 } """ @@ -30,15 +28,19 @@ Feature: Orders } """ - @failing @gh-469 + @failing Scenario: Checking escrow of order after creating a credit + # this scenario makes no since as you are attempting to credit + # out from an order when there is no money in the order itself + # I believe that you were attempting to copy a scenario + # that was adding money to the order first Given I have created an order And I have sufficient funds in my marketplace And I have tokenized a bank account And I POST to /bank_accounts/:bank_account_id/credits with the JSON API body: """ { - "order": "<%= @orders_id %>", + "order": ":order_id", "amount": 1234 } """ @@ -48,19 +50,18 @@ Feature: Orders And the fields on this order match: """ { - "amount_escrowed": 0 + "amount_escrowed": 1234 } """ - - @failing @gh-469 Scenario: Orders cannot be credited more than escrow balance Given I have created an order + And I have tokenized a bank account And I have tokenized a customer card And I make a POST request to the link "cards.debits" with the body: """ { - "order": "<%= @orders_id $>", + "order": ":order_id", "amount": 1234 } """ @@ -76,7 +77,7 @@ Feature: Orders And I POST to /bank_accounts/:bank_account_id/credits with the JSON API body: """ { - "order": "<%= @orders_id %>", + "order": ":order_id", "amount": 2000 } """ @@ -85,11 +86,10 @@ Feature: Orders And the fields on this error match: """ { - "category_code": "account-insufficient-funds" + "category_code": "insufficient-funds" } """ - @failing @gh-469 Scenario: Create a refund Given I have created an order And I have tokenized a customer card @@ -106,23 +106,26 @@ Feature: Orders } """ - - @failing @gh-469 + @failing Scenario: Create a reversal Given I have created an order And I have tokenized a customer card And I make a POST request to /customers/:customer_id/orders with the body: """ { - "order": "<%= @orders_id %>", + "order": ":order_id", "amount": 1234 } """ + # this bank account does not automatically succeed, which means that the + # credits can not be reversed instantly + # this is how it would normally happen in a prod marketplace + # but there are some ba numbers that can be reversed And I have tokenized a bank account And I make a POST request to the link "bank_accounts.credits" with the body: """ { - "order": "<%= @orders_id %>", + "order": ":order_id", "amount": 1234 } """ @@ -142,14 +145,14 @@ Feature: Orders """ - @failing @gh-471 + @failing @gh-471 @focusz Scenario: Create a failed refund when insufficient funds are in order escrow Given I have created an order And I have tokenized a card When I make a POST request to the link "cards.debits" with the body: """ { - "order": "<%= @orders_id %>", + "order": ":order_id", "amount": 1234 } """ @@ -157,7 +160,7 @@ Feature: Orders Then I make a POST request to the link "bank_accounts.credits" with the body: """ { - "order": "<%= @orders_id %>", + "order": ":order_id", "amount": 1234 } """ @@ -197,7 +200,7 @@ Feature: Orders """ { "amount": 10000, - "order": "<%= @orders_id %>", + "order": ":order_id", } """ @@ -214,7 +217,7 @@ Feature: Orders """ { "amount": 10000, - "order": "<%= @orders_id %>", + "order": ":order_id", } """ Then I should get a 201 Created status code @@ -235,7 +238,7 @@ Feature: Orders And I make a POST request to the link "cards.debits" with the body: """ { - "order": "<%= @orders_id %>", + "order": ":order_id", "amount": 1234 } """ @@ -246,7 +249,7 @@ Feature: Orders """ { "amount": 1234, - "order": "<%= @orders_id %>" + "order": ":order_id" } """ Then I should get a 409 status code diff --git a/features/rest/bank_accounts.feature b/features/rest/bank_accounts.feature index 847907c..f7bd652 100644 --- a/features/rest/bank_accounts.feature +++ b/features/rest/bank_accounts.feature @@ -5,7 +5,6 @@ Feature: Bank accounts but to debit from a bank account, micro deposit verifications are required. - @failing Scenario: Tokenize a bank account When I POST to /bank_accounts without my secret key with the JSON API body: """ @@ -23,28 +22,24 @@ Feature: Bank accounts Then I should get a 200 OK status code And the response is valid according to the "bank_accounts" schema - @failing Scenario: Retrieve a bank account Given I have tokenized a bank account When I GET to /bank_accounts/:bank_account_id Then I should get a 200 OK status code And the response is valid according to the "bank_accounts" schema - @failing Scenario: List bank accounts Given I have tokenized more than one bank account When I GET to /bank_accounts Then I should get a 200 OK status code And the response is valid according to the "bank_accounts" schema - @failing Scenario: Unstore a bank account Given I have tokenized a bank account When I DELETE to /bank_accounts/:bank_account_id Then I should get a 204 OK status code And there should be no response body - @failing Scenario: Update a bank account Given I have tokenized a bank account When I PUT to /bank_accounts/:bank_account_id with the JSON API body: diff --git a/features/rest/cards.feature b/features/rest/cards.feature index 5d66b90..cc261f4 100644 --- a/features/rest/cards.feature +++ b/features/rest/cards.feature @@ -8,7 +8,6 @@ Feature: Tokenize a credit card For more on tokenization as a concept, see ['tokenization' on Wikipedia.](http://en.wikipedia.org/wiki/Tokenization_%28data_security%29) - @failing Scenario: Tokenize a card without a secret key Cards are able to be tokenized without sending along a secret key. When this happens, the customer gets less information than if the key was sent. @@ -28,14 +27,12 @@ Feature: Tokenize a credit card Then I should get a 200 OK status code And the response is valid according to the "cards" schema - @failing Scenario: Retrieve a card Given I have tokenized a card When I GET to /cards/:card_id giving the card_id Then I should get a 200 OK status code And the response is valid according to the "cards" schema - @failing Scenario: List cards Given I have tokenized more than one card When I GET to /cards @@ -48,7 +45,6 @@ Feature: Tokenize a credit card Then I should get a 204 OK status code And there should be no response body - @failing Scenario: Update a card Given I have tokenized a card When I PUT to /cards/:card_id giving the card_id, with the JSON API body: @@ -62,14 +58,13 @@ Feature: Tokenize a credit card Then I should get a 200 OK status code And the response is valid according to the "cards" schema - @failing Scenario: Debit a card Given I have tokenized a card When I POST to /cards/:card_id/debits giving the card_id, with the JSON API body: """ { - "amount": "1" + "amount": "50" } """ Then I should get a 201 Created status code - And the response is valid according to the "_models/debit" schema + And the response is valid according to the "debits" schema diff --git a/features/rest/customers.feature b/features/rest/customers.feature index 2794f6b..6e2c1e3 100644 --- a/features/rest/customers.feature +++ b/features/rest/customers.feature @@ -39,7 +39,6 @@ Feature: Customers Then I should get a 204 OK status code And there should be no response body - @failing Scenario: Add a card to a customer Given I have tokenized a card And I have created a customer @@ -62,7 +61,6 @@ Feature: Customers } """ - @failing Scenario: Add a bank account to a customer Given I have tokenized a bank account And I have created a customer diff --git a/features/step_definitions/debugging.rb b/features/step_definitions/debugging.rb index fd1a0ea..7f960d2 100644 --- a/features/step_definitions/debugging.rb +++ b/features/step_definitions/debugging.rb @@ -1,6 +1,7 @@ Then(/^debug$/) do puts "HTTP status code: #{@client.last_code}" puts "HTTP body: #{@client.last_body}" + puts "hydrate tokens: #{@client.hydrate_tokens}" end require 'logger' @@ -10,4 +11,3 @@ When(/^logging is enabled$/) do $logger.level = Logger::DEBUG end - diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 97acf56..4fdfb08 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -52,13 +52,12 @@ def env end When(/^I make a (\w+) request to the link "(.*?)" with the body:$/) do |verb, keys, body| - puts @client.last_body - body = ERB.new(body).result(binding) + body = ERB.new(@client.hydrater(body)).result(binding) $logger.debug("Requesting hydrated: #{@client.hydrater(@client.last_body["links"][keys])}") body = @client.send(verb.downcase, @client.hydrater(@client.last_body["links"][keys]), JSON.parse(body), env) @credit_id = @client['credits']['id'] rescue nil @cards_id = @client['cards']['id'] rescue nil - @client.add_hydrate(:cards_id, @cards_id) if @cards_id + @client.add_hydrate(:card_id, @cards_id) if @cards_id @debit_url = @client['debits']['href'] rescue nil body end @@ -80,7 +79,7 @@ def env $logger.debug("Requesting hydrated: #{@client.hydrater(@client.last_body["links"][keys])}") body = @client.send(verb.downcase, @client.hydrater(@client.last_body["links"][keys]), {}, env) @cards_id = @client['cards']['id'] rescue nil - @client.add_hydrate(:cards_id, @cards_id) if @cards_id + @client.add_hydrate(:card_id, @cards_id) if @cards_id @credit_id = @client['credits']['id'] rescue nil body end @@ -115,7 +114,7 @@ def env end When(/^I POST to (\/\S*) with the JSON API body:$/) do |url, body| - body = @client.post(@client.hydrater(url), body) + body = @client.post(@client.hydrater(url), @client.hydrater(body)) @credit_id = @client['credits']['id'] rescue nil @card_id = @client['cards']['id'] rescue nil body @@ -126,7 +125,7 @@ def env end When(/^I PATCH to (\/\S*) with the JSON API body:$/) do |url, body| - @client.patch(@client.hydrater(url), body) + @client.patch(@client.hydrater(url), @client.hydrater(body)) end require 'json-schema' diff --git a/features/step_definitions/orders.rb b/features/step_definitions/orders.rb index 22f1c83..c836adf 100644 --- a/features/step_definitions/orders.rb +++ b/features/step_definitions/orders.rb @@ -1,4 +1,5 @@ -Given(/^I have created an order$/) do +Given(/^I have created an order$/) do step 'I have created a customer' - step 'I make a POST request to /customers/:customer_id/orders' -end + @client.post(@client.hydrater('/customers/:customer_id/orders'), {}) + @client.add_hydrate :order_id, @client['id'] +end diff --git a/lib/balanced/tiny_client.rb b/lib/balanced/tiny_client.rb index 93b106a..6b81e21 100644 --- a/lib/balanced/tiny_client.rb +++ b/lib/balanced/tiny_client.rb @@ -4,6 +4,7 @@ module Balanced class TinyClient attr_reader :api_secret, :root_url attr_reader :responses + attr_reader :hydrate_tokens attr_writer :running def initialize(api_secret, accept_header, root_url) From 89da538a3669a0cd744deb6d68638ff6c7c03b39 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 00:00:02 +0000 Subject: [PATCH 103/154] making progress on 'existing buyer makes a purchase with an existing card. Yet another #469 victim. --- features/checkout_flow.feature | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/features/checkout_flow.feature b/features/checkout_flow.feature index b9ee7b6..e630f5d 100644 --- a/features/checkout_flow.feature +++ b/features/checkout_flow.feature @@ -181,13 +181,14 @@ Feature: Credit cards } """ - @failing + @failing @gh-469 Scenario: Existing buyer makes a purchase with an existing card + Given I have created a customer When I make a GET request to /customers/:customer_id Then I should get a 200 OK status code And the response is valid according to the "customers" schema - Then I make a GET request to "customers.source" + Then I make a GET request to the link "customers.source" Then I should get a 200 OK status code And the response is valid according to the "cards" schema @@ -209,34 +210,36 @@ Feature: Credit cards """ Then I should get a 201 CREATED status code And the response is valid according to the "orders" schema + And the fields on this order match: """ { - "links":{ "merchant": "#{@customers_id}" } + "links":{ "merchant": "<%= @customers_id %>" } } """ - When I make a POST request to "customers.debits" with the body + When I make a POST request to the link "customers.debits" with the body: """ { "amount": 10000, - "order": "#{@orders_id}", + "order": "<%= @orders_id %>", "appears_on_statement_as": "Vaunte-Alice Ryan" } """ Then I should get a 201 CREATED status code And the response is valid according to the "debits" schema + And the fields on this debit match: """ { "description": "Catherine Malandrino Black Top", "appears_on_statement_as": "BAL*Vaunte-Alice Ryan", "status": "succeeded", "links": { - "order": "#{@orders_id}", + "order": "<%= @orders_id %>", } } """ - When I make a PUT request to /orders/:order_id with the JSON API body: + When I PUT to /orders/:order_id with the JSON API body: """ { "meta": { @@ -248,6 +251,7 @@ Feature: Credit cards """ Then I should get a 200 OK status code And the response is valid according to the "orders" schema + And the fields on this order match: """ { "meta": { From a4a51d74eabfa01f9b408de807a941069ae60bcd Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 00:41:54 +0000 Subject: [PATCH 104/154] Making progress on 'new buyer makes a purchase' Another #469. --- features/checkout_flow.feature | 29 +++++++++++++++---------- features/step_definitions/http_steps.rb | 9 ++++++-- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/features/checkout_flow.feature b/features/checkout_flow.feature index e630f5d..2f893f2 100644 --- a/features/checkout_flow.feature +++ b/features/checkout_flow.feature @@ -262,9 +262,9 @@ Feature: Credit cards } """ - @failing + @failing @gh-469 Scenario: New buyer makes a purchase - When I make a POST request to /cards with the JSON API body: + When I POST to /cards with the JSON API body: """ { "name": "Darius the Great", @@ -287,6 +287,7 @@ Feature: Credit cards When I make a GET request to /cards/:cards_id Then I should get a 200 OK status code And the response is valid according to the "cards" schema + And the fields on this card match: """ { "avs_street_match": "yes", @@ -295,29 +296,31 @@ Feature: Credit cards } """ - When I make a POST request to /customers + When I make a POST request to /customers with the body: """ { "name": "Darius the Great", "email": "darius.great@gmail.com", - "source": "#{@cards_id}", + "source": "<%= @card_id %>", "meta": { "ip_address": "174.240.15.249" } } """ + Then I should get a 201 OK status code And the response is valid according to the "customers" schema + And the fields on this customer match: """ { "links": { - "source": "#{@cards_id}", - "destination": null, + "source": "<%= @card_id %>", + "destination": null } } """ - When I make a POST request to /customers/:customer_id/orders with the body + When I make a POST request to /customers/:customer_id/orders with the body: """ { "description": "Catherine Malandrino Black Top", @@ -335,22 +338,24 @@ Feature: Credit cards """ Then I should get a 201 CREATED status code And the response is valid according to the "orders" schema + And the fields on this order match: """ { - "links":{ "merchant": "#{@customers_id}" } + "links":{ "merchant": "<%= @customers_id %>" } } """ - When I make a POST request to "customers.debits" with the body + When I make a POST request to the link "customers.debits" with the body: """ { "amount": 10000, - "order": "#{@orders_id}", + "order": "<%= @orders_id %>", "appears_on_statement_as": "Vaunte-Alice Ryan" } """ Then I should get a 201 CREATED status code And the response is valid according to the "debits" schema + And the fields on this debit match: """ { "description": "Catherine Malandrino Black Top", @@ -363,7 +368,7 @@ Feature: Credit cards """ - When I make a PUT request to /orders/:order_id with the JSON API body: + When I PUT to /orders/:order_id with the JSON API body: """ { "meta": { @@ -373,8 +378,10 @@ Feature: Credit cards } } """ + Then I should get a 200 OK status code And the response is valid according to the "orders" schema + And the fields on this order match: """ { "meta": { diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 4fdfb08..8f13f03 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -11,6 +11,7 @@ end When(/^I (\w+) to (\/\S*?) with the body:$/) do |verb, url, body| + body = ERB.new(body).result(binding) $logger.debug("Making request to #{url}") $logger.debug("hydrated: #{@client.hydrater(url)}") body = @client.hydrater body @@ -71,7 +72,6 @@ def env When(/^I fetch the (.*+)$/) do |resource| resource = resource.gsub(/\s/, "_") id = instance_variable_get("@#{resource}_id") - puts id @client.get("/resources/#{id}") end @@ -117,6 +117,7 @@ def env body = @client.post(@client.hydrater(url), @client.hydrater(body)) @credit_id = @client['credits']['id'] rescue nil @card_id = @client['cards']['id'] rescue nil + @client.add_hydrate(:cards_id, @card_id) if @card_id body end @@ -147,11 +148,13 @@ def env end def checker(from, of, nesting) - assert_not_nil of, nesting + assert_not_nil from, nesting from.each_pair do |key, val| if val.is_a? String or val.is_a? Integer assert_equal val, of[key], "#{nesting}>#{key}" + elsif val.nil? + assert_nil of[key] else checker val, of[key], "#{nesting}>#{key}" end @@ -159,11 +162,13 @@ def checker(from, of, nesting) end Then(/^the fields on this (.*) match:$/) do |resource, against| + against = ERB.new(against).result(binding) checker JSON.parse(@client.hydrater against), @client["#{resource}s"], '' assert_equal @client.last_body["#{resource}s"].size, 1 end Then(/^the fields on these (.*) match:$/) do |resource, against| + against = ERB.new(against).result(binding) against = JSON.parse(@client.hydrater against) @client.last_body[resource].each do |body| checker against, body, '' From e7f8430ed627d75dc2caaee8181abb22dccd0faa Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 17:41:33 +0000 Subject: [PATCH 105/154] Progress on existing buyer makes a purchase with a new card. Closes #476. --- features/checkout_flow.feature | 2 +- features/step_definitions/http_steps.rb | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/features/checkout_flow.feature b/features/checkout_flow.feature index 2f893f2..2f93e37 100644 --- a/features/checkout_flow.feature +++ b/features/checkout_flow.feature @@ -50,7 +50,7 @@ Feature: Credit cards } """ - @failing @gh-476 @focus + @failing @gh-469 Scenario: Existing buyer makes a purchase with a new card Given I have created a customer When I make a GET request to /customers/:customer_id diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 8f13f03..7dadfae 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -126,7 +126,8 @@ def env end When(/^I PATCH to (\/\S*) with the JSON API body:$/) do |url, body| - @client.patch(@client.hydrater(url), @client.hydrater(body)) + body = ERB.new(@client.hydrater(body)).result(binding) + @client.patch(@client.hydrater(url), body) end require 'json-schema' From 3cb89582f28f8931cfa25eb74b30a9d3a997e941 Mon Sep 17 00:00:00 2001 From: Richie Serna Date: Tue, 14 Jan 2014 19:27:01 +0000 Subject: [PATCH 106/154] Add credit card scenarios. --- features/credit_cards.feature | 448 ++++++++++++++++++++++++++++++++++ 1 file changed, 448 insertions(+) create mode 100644 features/credit_cards.feature diff --git a/features/credit_cards.feature b/features/credit_cards.feature new file mode 100644 index 0000000..e554605 --- /dev/null +++ b/features/credit_cards.feature @@ -0,0 +1,448 @@ +Feature: Credit cards + + @failing + Scenario: Add a card to a customer + Given I have created a customer + When I make a PATCH request to the link "href" with the body: + """ + { + "op": "replace", + "path": "/cards/0/links/customer", + "value": "#{@customers_id}" + } + """ + Then I should get a 200 OK status code + And the response is valid according to the "cards" schema + """ + { + "links": { "customer": "#{@customers_id}" } + } + """ + + @failing + Scenario: AVS Postal code matches + When I make a POST request to /cards with the body: + """ + { + "number": "4111111111111111", + "expiration_month": 12, + "expiration_year": 2016, + "address": { + "postal_code": "94301" + } + } + """ + + Then I should get a 201 CREATED status code + And the response is valid according to the "cards" schema + """ + { + "avs_postal_match": "yes" + } + """ + + @failing + Scenario: AVS Postal code does not match + When I make a POST request to /cards with the body: + """ + { + "number": "4111111111111111", + "expiration_month": 12, + "expiration_year": 2016, + "address": { + "postal_code": "90210" + } + } + """ + + Then I should get a 201 CREATED status code + And the response is valid according to the "cards" schema + """ + { + "avs_postal_match": "no" + } + """ + + @failing + Scenario: AVS Postal code is unsupported + When I make a POST request to /cards with the body: + """ + { + "number": "4111111111111111", + "expiration_month": 12, + "expiration_year": 2016, + "address": { + "postal_code": "90211" + } + } + """ + + Then I should get a 201 CREATED status code + And the response is valid according to the "cards" schema + """ + { + "avs_postal_match": "unsupported" + } + """ + + @failing + Scenario: AVS Postal code is unused + When I make a POST request to /cards with the body: + """ + { + "number": "4111111111111111", + "expiration_month": 12, + "expiration_year": 2016, + } + """ + + Then I should get a 201 CREATED status code + And the response is valid according to the "cards" schema + """ + { + "avs_postal_match": null + } + """ + + @failing + Scenario: AVS street matches + When I make a POST request to /cards with the body: + """ + { + "number": "4111111111111111", + "expiration_month": 12, + "expiration_year": 2016, + "address": { + "line1": "965 Mission St", + "postal_code": "94103" + } + } + """ + + Then I should get a 201 CREATED status code + And the response is valid according to the "cards" schema + """ + { + "avs_street_match": "yes" + } + """ + + @failing + Scenario: AVS street does not matches + When I make a POST request to /cards with the body: + """ + { + "number": "4111111111111111", + "expiration_month": 12, + "expiration_year": 2016, + "address": { + "line1": "21 Jump St", + "postal_code": "90210" + } + } + """ + + Then I should get a 201 CREATED status code + And the response is valid according to the "cards" schema + """ + { + "avs_street_match": "no" + } + """ + + @failing + Scenario: AVS street match is null + When I make a POST request to /cards with the body: + """ + { + "number": "4111111111111111", + "expiration_month": 12, + "expiration_year": 2016, + } + """ + + Then I should get a 201 CREATED status code + And the response is valid according to the "cards" schema + """ + { + "avs_street_match": null + } + """ + + @failing + Scenario: Detect a Visa card brand + When I make a POST request to /cards with the body: + """ + { + "number": "4111 1111 1111 1111", + "expiration_month": 12, + "expiration_year": 2016, + } + """ + + Then I should get a 201 CREATED status code + And the response is valid according to the "cards" schema + """ + { + "brand": "Visa" + } + """ + + @failing + Scenario: Detect a Mastercard card brand + When I make a POST request to /cards with the body: + """ + { + "number": "5105 1051 0510 5100", + "expiration_month": 12, + "expiration_year": 2016, + } + """ + + Then I should get a 201 CREATED status code + And the response is valid according to the "cards" schema + """ + { + "brand": "MasterCard" + } + """ + + @failing + Scenario: Detect an American Express card brand + When I make a POST request to /cards with the body: + """ + { + "number": "3782 822463 10005", + "expiration_month": 12, + "expiration_year": 2016, + } + """ + + Then I should get a 201 CREATED status code + And the response is valid according to the "cards" schema + """ + { + "brand": "American Express" + } + """ + + @failing + Scenario: Detect an Discover card brand + When I make a POST request to /cards with the body: + """ + { + "number": "6011 1111 1111 1117", + "expiration_month": 12, + "expiration_year": 2016, + } + """ + + Then I should get a 201 CREATED status code + And the response is valid according to the "cards" schema + """ + { + "brand": "Discover" + } + """ + + @failing + Scenario: Retrieving a card + Given I have tokenized a card + When I make a GET request to /cards/:card_id + Then I should get a 200 OK status code + And the response is valid according to the "cards" schema + """ + { + "name": null, + "number": "xxxxxxxxxxxx1111", + "expiration_month": 12, + "expiration_year": 2016, + "cvv": xxx, + "cvv_match": "yes", + "cvv_result": "Match", + "address": { + "line1": "965 Mission St", + "line2": null, + "city": null, + "state": null, + "postal_code": "94103", + "country_code": null + }, + "avs_street_match": "yes", + "avs_postal_match": "yes", + "avs_result": "Postal code matches, but street address not verified.", + "brand": "Visa", + "meta": {} + } + """ + + @failing + Scenario: Tokenizing a card + When I make a POST request to /cards with the body: + """ + { + "number": "4111 1111 1111 1111", + "expiration_month": "12", + "expiration_year": "2016" + } + """ + Then I should get a 201 CREATED status code + And the response is valid according to the "cards" schema + """ + { + "expiration_month": 12, + "expiration_year": 2016 + } + """ + + When I make a POST request to /cards with the body: + """ + { + "name": "Frida Kahlo", + "number": "4111 1111 1111 1111", + "expiration_month": 12, + "expiration_year": 2016 + } + """ + Then I should get a 201 CREATED status code + And the response is valid according to the "cards" schema + """ + { + "name": "Frida Kahlo" + } + """ + + When I make a POST request to /cards with the body: + """ + { + "number": "4111 1111 1111 1111", + "expiration_month": 12, + "expiration_year": 2016, + "address": { + "line1": "7 Bis Rue de l'Abbé de l'Épée", + "line2": "Apt 4", + "city": "Versailles", + "postal_code": "78000", + "country_code": "FR" + } + } + """ + Then I should get a 201 CREATED status code + And the response is valid according to the "cards" schema + """ + { + "address": { + "line1": "7 Bis Rue de l'Abbé de l'Épée", + "line2": "Apt 4", + "city": "Versailles", + "state": null, + "postal_code": "78000", + "country_code": "FR" + } + } + """ + + @failing + Scenario: Tokenization fails luhn test + When I make a POST request to /cards with the body: + """ + { + "number": "4111 1111 1111 1112", + "expiration_month": 12, + "expiration_year": 2016 + } + """ + Then I should get a 409 status code + And the response is valid according to the "errors" schema + And the fields on this error match: + """ + { + "category_code": "card-not-validated" + } + """ + + @failing + Scenario: Unstore a card + Given I have tokenized a card + When I make a DELETE request to /cards/:card_id + Then I should get a 204 status code + + + @failing + Scenario: CVV matches + When I make a POST request to /cards with the body: + """ + { + "number": "4111111111111111", + "expiration_month": 12, + "expiration_year": 2016, + "cvv": "123" + } + """ + + Then I should get a 201 CREATED status code + And the response is valid according to the "cards" schema + """ + { + "cvv_match": "yes" + } + """ + + @failing + Scenario: CVV does not match + When I make a POST request to /cards with the body: + """ + { + "number": "4111111111111111", + "expiration_month": 12, + "expiration_year": 2016, + "cvv": "902" + } + """ + + Then I should get a 201 CREATED status code + And the response is valid according to the "cards" schema + """ + { + "cvv_match": "no" + } + """ + + @failing + Scenario: CVV is unsupported + When I make a POST request to /cards with the body: + """ + { + "number": "4111111111111111", + "expiration_month": 12, + "expiration_year": 2016, + "cvv": "901" + } + """ + + Then I should get a 201 CREATED status code + And the response is valid according to the "cards" schema + """ + { + "cvv_match": "unsupported" + } + """ + + @failing + Scenario: CVV is unused + When I make a POST request to /cards with the body: + """ + { + "number": "4111111111111111", + "expiration_month": 12, + "expiration_year": 2016 + } + """ + + Then I should get a 201 CREATED status code + And the response is valid according to the "cards" schema + """ + { + "cvv_match": null + } + """ From 8a3ffbffdd2c8f3ffe6bb247242ebe61d4f93f6d Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 19:37:49 +0000 Subject: [PATCH 107/154] making progress on 'adding a card to a customer' --- features/credit_cards.feature | 13 +++++++------ lib/balanced/tiny_client.rb | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index e554605..c6afd69 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -1,21 +1,22 @@ Feature: Credit cards - @failing + @failing @gh-479 Scenario: Add a card to a customer Given I have created a customer - When I make a PATCH request to the link "href" with the body: + When I make a PATCH request to the href "href" with the body: """ - { + [{ "op": "replace", "path": "/cards/0/links/customer", - "value": "#{@customers_id}" - } + "value": "<%= @customer_id %>" + }] """ Then I should get a 200 OK status code And the response is valid according to the "cards" schema + And the fields on this card match: """ { - "links": { "customer": "#{@customers_id}" } + "links": { "customer": "<%= @customer_id %>" } } """ diff --git a/lib/balanced/tiny_client.rb b/lib/balanced/tiny_client.rb index 6b81e21..45bcf54 100644 --- a/lib/balanced/tiny_client.rb +++ b/lib/balanced/tiny_client.rb @@ -68,7 +68,7 @@ def put(endpoint, body, env={}) response end - def patch(endpoint, body) + def patch(endpoint, body, env={}) body = JSON.parse(body) if body.is_a? String options = { headers: { From 0fb808b540c092856787b91730ebda3ea4e08e57 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 22:37:51 +0000 Subject: [PATCH 108/154] New feature: AVS postal code matches --- features/credit_cards.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index c6afd69..1f9655c 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -20,7 +20,6 @@ Feature: Credit cards } """ - @failing Scenario: AVS Postal code matches When I make a POST request to /cards with the body: """ @@ -36,6 +35,7 @@ Feature: Credit cards Then I should get a 201 CREATED status code And the response is valid according to the "cards" schema + And the fields on this card match: """ { "avs_postal_match": "yes" From 0c1d8101f465408d2726c81412a44746cc993d8a Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 22:38:54 +0000 Subject: [PATCH 109/154] New feature: AVS postal code does not match --- features/credit_cards.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index 1f9655c..be41ef6 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -42,7 +42,6 @@ Feature: Credit cards } """ - @failing Scenario: AVS Postal code does not match When I make a POST request to /cards with the body: """ @@ -58,6 +57,7 @@ Feature: Credit cards Then I should get a 201 CREATED status code And the response is valid according to the "cards" schema + And the fields on this card match: """ { "avs_postal_match": "no" From 7de997df8bef9ef4e0c01b1f2f4b80be3986bbbe Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 22:39:48 +0000 Subject: [PATCH 110/154] New scenario: AVS Postal card is unsupported --- features/credit_cards.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index be41ef6..79e20a8 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -64,7 +64,6 @@ Feature: Credit cards } """ - @failing Scenario: AVS Postal code is unsupported When I make a POST request to /cards with the body: """ @@ -80,6 +79,7 @@ Feature: Credit cards Then I should get a 201 CREATED status code And the response is valid according to the "cards" schema + And the fields on this card match: """ { "avs_postal_match": "unsupported" From b96c9d9cf839c1e064e928a67d16692c013a0de2 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 22:41:51 +0000 Subject: [PATCH 111/154] Making progress on 'AVS postal code is unused. #438 will fix this. --- features/credit_cards.feature | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index 79e20a8..91a2abe 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -86,19 +86,20 @@ Feature: Credit cards } """ - @failing + @failing @gh-438 Scenario: AVS Postal code is unused When I make a POST request to /cards with the body: """ { "number": "4111111111111111", "expiration_month": 12, - "expiration_year": 2016, + "expiration_year": 2016 } """ Then I should get a 201 CREATED status code And the response is valid according to the "cards" schema + And the fields on this card match: """ { "avs_postal_match": null From 5381186bf806dded2ee35a352174e87edfd57542 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 22:43:02 +0000 Subject: [PATCH 112/154] New feature: AVS street matches --- features/credit_cards.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index 91a2abe..e80d362 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -106,7 +106,6 @@ Feature: Credit cards } """ - @failing Scenario: AVS street matches When I make a POST request to /cards with the body: """ @@ -123,6 +122,7 @@ Feature: Credit cards Then I should get a 201 CREATED status code And the response is valid according to the "cards" schema + And the fields on this card match: """ { "avs_street_match": "yes" From 87aafe925dc77ead1e977eac6d7d7244691b4e2b Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 22:43:51 +0000 Subject: [PATCH 113/154] New feature: AVS street does not match --- features/credit_cards.feature | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index e80d362..eb679fe 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -129,8 +129,7 @@ Feature: Credit cards } """ - @failing - Scenario: AVS street does not matches + Scenario: AVS street does not match When I make a POST request to /cards with the body: """ { @@ -146,6 +145,7 @@ Feature: Credit cards Then I should get a 201 CREATED status code And the response is valid according to the "cards" schema + And the fields on this card match: """ { "avs_street_match": "no" From ec98ab48e6e7f9773fcdcaaaace660ffcf1f3349 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 22:46:20 +0000 Subject: [PATCH 114/154] Making progress on 'avs street match is null' #438 will fix that. --- features/credit_cards.feature | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index eb679fe..d315e23 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -152,19 +152,20 @@ Feature: Credit cards } """ - @failing + @failing @gh-438 Scenario: AVS street match is null When I make a POST request to /cards with the body: """ { "number": "4111111111111111", "expiration_month": 12, - "expiration_year": 2016, + "expiration_year": 2016 } """ Then I should get a 201 CREATED status code And the response is valid according to the "cards" schema + And the fields on this card match: """ { "avs_street_match": null From 84903e0d399a9439aee663d00105c94d273b1ea8 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 22:49:29 +0000 Subject: [PATCH 115/154] Progress on 'detect a visa card brand' #438 fixes --- features/credit_cards.feature | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index d315e23..f5395c5 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -172,19 +172,20 @@ Feature: Credit cards } """ - @failing + @failing @gh-438 Scenario: Detect a Visa card brand When I make a POST request to /cards with the body: """ { "number": "4111 1111 1111 1111", "expiration_month": 12, - "expiration_year": 2016, + "expiration_year": 2016 } """ Then I should get a 201 CREATED status code And the response is valid according to the "cards" schema + And the fields on this card match: """ { "brand": "Visa" From 9a972aa129e6f23de0bbc2f648e85613d223613c Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 22:50:36 +0000 Subject: [PATCH 116/154] Make progress on 'detect a mastercard brand' #438 fixes --- features/credit_cards.feature | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index f5395c5..c8ebaf4 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -192,19 +192,20 @@ Feature: Credit cards } """ - @failing + @failing @gh-438 Scenario: Detect a Mastercard card brand When I make a POST request to /cards with the body: """ { "number": "5105 1051 0510 5100", "expiration_month": 12, - "expiration_year": 2016, + "expiration_year": 2016 } """ Then I should get a 201 CREATED status code And the response is valid according to the "cards" schema + And the fields on this card match: """ { "brand": "MasterCard" From f850887616e80805bab1fddf3c498aaa4d2ef556 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 22:51:58 +0000 Subject: [PATCH 117/154] Make progress on 'detect an amex brand' #438 fixes --- features/credit_cards.feature | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index c8ebaf4..cf9ba19 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -212,19 +212,20 @@ Feature: Credit cards } """ - @failing + @failing @gh-438 Scenario: Detect an American Express card brand When I make a POST request to /cards with the body: """ { "number": "3782 822463 10005", "expiration_month": 12, - "expiration_year": 2016, + "expiration_year": 2016 } """ Then I should get a 201 CREATED status code And the response is valid according to the "cards" schema + And the fields on this card match: """ { "brand": "American Express" From ae18309d77c1ae0706dcfc7876ded1f23cd6b4c5 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 22:53:16 +0000 Subject: [PATCH 118/154] make progress on 'detect a discover card' --- features/credit_cards.feature | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index cf9ba19..c1de724 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -232,19 +232,20 @@ Feature: Credit cards } """ - @failing - Scenario: Detect an Discover card brand + @failing @gh-438 + Scenario: Detect a Discover card brand When I make a POST request to /cards with the body: """ { "number": "6011 1111 1111 1117", "expiration_month": 12, - "expiration_year": 2016, + "expiration_year": 2016 } """ Then I should get a 201 CREATED status code And the response is valid according to the "cards" schema + And the fields on this card match: """ { "brand": "Discover" From 761af4e9ecfa4690e04e1db90bb31cdff827802c Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 23:01:06 +0000 Subject: [PATCH 119/154] Make some progress on retrieving card info #480 is tracking this. --- features/credit_cards.feature | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index c1de724..eddb00d 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -252,19 +252,20 @@ Feature: Credit cards } """ - @failing + @failing @gh-480 Scenario: Retrieving a card Given I have tokenized a card When I make a GET request to /cards/:card_id Then I should get a 200 OK status code And the response is valid according to the "cards" schema + And the fields on this card match: """ { "name": null, "number": "xxxxxxxxxxxx1111", "expiration_month": 12, "expiration_year": 2016, - "cvv": xxx, + "cvv": "xxx", "cvv_match": "yes", "cvv_result": "Match", "address": { From 80b8eb3ba2f47b182909623e66ca90da99d67e57 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 23:05:31 +0000 Subject: [PATCH 120/154] Progress on 'tokenizing a card' #438 is the problem. --- features/credit_cards.feature | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index eddb00d..939a292 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -284,7 +284,7 @@ Feature: Credit cards } """ - @failing + @failing @gh-438 Scenario: Tokenizing a card When I make a POST request to /cards with the body: """ @@ -296,6 +296,7 @@ Feature: Credit cards """ Then I should get a 201 CREATED status code And the response is valid according to the "cards" schema + And the fields on this card match: """ { "expiration_month": 12, @@ -314,6 +315,7 @@ Feature: Credit cards """ Then I should get a 201 CREATED status code And the response is valid according to the "cards" schema + And the fields on this card match: """ { "name": "Frida Kahlo" @@ -337,6 +339,7 @@ Feature: Credit cards """ Then I should get a 201 CREATED status code And the response is valid according to the "cards" schema + And the fields on this card match: """ { "address": { From e51d3a94d8206c3c96f809b4ae8189d168275079 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 23:17:42 +0000 Subject: [PATCH 121/154] Making progress on luhn test failure --- features/credit_cards.feature | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index 939a292..7526cae 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -353,12 +353,12 @@ Feature: Credit cards } """ - @failing + @failing @gh-481 Scenario: Tokenization fails luhn test When I make a POST request to /cards with the body: """ { - "number": "4111 1111 1111 1112", + "number": "4111111111111112", "expiration_month": 12, "expiration_year": 2016 } From b47d8d0681c6eea1d3615074fe61a4afcb095fb8 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 23:23:19 +0000 Subject: [PATCH 122/154] new feature: unstore a card --- features/credit_cards.feature | 1 - 1 file changed, 1 deletion(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index 7526cae..dec5161 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -372,7 +372,6 @@ Feature: Credit cards } """ - @failing Scenario: Unstore a card Given I have tokenized a card When I make a DELETE request to /cards/:card_id From 7924eb0f0c1d024b1990ef242bfec225aaa8dc7a Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 23:33:37 +0000 Subject: [PATCH 123/154] New feature: CVV matches --- features/credit_cards.feature | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index dec5161..a249eba 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -378,7 +378,7 @@ Feature: Credit cards Then I should get a 204 status code - @failing + @failing @gh-438 Scenario: CVV matches When I make a POST request to /cards with the body: """ @@ -392,6 +392,7 @@ Feature: Credit cards Then I should get a 201 CREATED status code And the response is valid according to the "cards" schema + And the fields on this card match: """ { "cvv_match": "yes" From 4c454ed986ff5a2c5ac8981154840066831700f6 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 23:37:00 +0000 Subject: [PATCH 124/154] progress on 'CVV doesn't match' #438 fixes this. --- features/credit_cards.feature | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index a249eba..f9c24df 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -399,7 +399,7 @@ Feature: Credit cards } """ - @failing + @failing @gh-438 Scenario: CVV does not match When I make a POST request to /cards with the body: """ @@ -413,6 +413,7 @@ Feature: Credit cards Then I should get a 201 CREATED status code And the response is valid according to the "cards" schema + And the fields on this card match: """ { "cvv_match": "no" From 91847c7a5b643a51f9c3eca52511ff4d72069332 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 23:48:02 +0000 Subject: [PATCH 125/154] Progress on 'CVV is unsupported' #438 strikes again --- features/credit_cards.feature | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index f9c24df..cd043bf 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -420,7 +420,7 @@ Feature: Credit cards } """ - @failing + @failing @gh-438 Scenario: CVV is unsupported When I make a POST request to /cards with the body: """ @@ -434,6 +434,7 @@ Feature: Credit cards Then I should get a 201 CREATED status code And the response is valid according to the "cards" schema + And the fields on this card match: """ { "cvv_match": "unsupported" From 0af3c984b20b2e704bb0e13a8c776186d0ace35a Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 23:51:00 +0000 Subject: [PATCH 126/154] Making progress on CVV is unused #438 again --- features/credit_cards.feature | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index cd043bf..0fc3c21 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -441,7 +441,7 @@ Feature: Credit cards } """ - @failing + @failing @gh-438 Scenario: CVV is unused When I make a POST request to /cards with the body: """ @@ -454,6 +454,7 @@ Feature: Credit cards Then I should get a 201 CREATED status code And the response is valid according to the "cards" schema + And the fields on this card match: """ { "cvv_match": null From 2c2a4b0a9d3c4bbbbbfe1e34cd5dda0bf4569e59 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 14 Jan 2014 23:54:10 +0000 Subject: [PATCH 127/154] 'Add a card to a customer' now works From 9a9e03214a67dcdbfd1a07b8852e19d66ead6a44 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Wed, 15 Jan 2014 21:58:25 +0000 Subject: [PATCH 128/154] Feature fixed: Debit a card From 2e7a0e64595c624a28147eee08eeeab7e9ef029b Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Wed, 15 Jan 2014 22:51:09 +0000 Subject: [PATCH 129/154] Making progress on 'basic order flow' --- features/orders.feature | 80 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/features/orders.feature b/features/orders.feature index f9ac7a0..2767105 100644 --- a/features/orders.feature +++ b/features/orders.feature @@ -7,6 +7,86 @@ Feature: Orders Then I should get a 201 Created status code And the response is valid according to the "orders" schema + @failing @gh-469 + Scenario: Basic order flow + Given I have a customer with a tokenized bank account + When I make a POST request to /customers/:customer_id/orders + Then I should get a 201 Created status code + And the response is valid according to the "orders" schema + And the fields on this order match: + """ + { + "links": { "merchant": "<%= @customer_id %>" } + } + """ + + When I fetch the customer + And I make a POST request to the link "customers.debits" with the body: + """ + { + "amount": 10000, + "order": "<%= @order_href %>" + } + """ + + Then I should get a 201 Created status code + And the response is valid according to the "debits" schema + And the fields on this debit match: + """ + { + "links": { + "order": "<%= @order_id %>" + } + } + """ + + When I fetch the order + And I make a GET request to the link "orders.href" + + Then I should get a 200 OK status code + And the response is valid according to the "orders" schema + And the fields on this order match: + """ + { + "amount": 10000, + "amount_escrowed": 10000 + } + """ + + When I fetch the customer + And I make a POST request to the link "customers.credits" with the body: + """ + { + "amount": 10000, + "order": "<%= @order_href %>" + } + """ + + Then I should get a 201 Created status code + And the response is valid according to the "credits" schema + And the fields on this credit match: + """ + { + "links": { + "order": "<%= @order_id %>" + } + } + """ + + When I fetch the order + And I make a GET request to the link "orders.href" + + Then I should get a 200 OK status code + And the response is valid according to the "orders" schema + And the fields on this order match: + """ + { + "amount": 10000, + "amount_escrowed": 0 + } + """ + + @failing @gh-469 Scenario: Checking escrow of order after creating a debit Given I have created an order And I have tokenized a customer card From 84d3b4b85393bf10f1746395f0fcd92788903ccb Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Thu, 16 Jan 2014 00:14:54 +0000 Subject: [PATCH 130/154] New feature: card metadata --- features/credit_cards.feature | 155 ++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index 0fc3c21..14db03e 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -460,3 +460,158 @@ Feature: Credit cards "cvv_match": null } """ + + Scenario: Adding card metadata + Given I have tokenized a card + When I make a PATCH request to the href "href" with the body: + """ + [{ + "op": "add", + "path": "/cards/0/meta/asdf", + "value": "the value to be added" + }] + """ + Then I should get a 200 OK status code + And the response is valid according to the "cards" schema + And the fields on this card match: + """ + { "meta": { "asdf": "the value to be added" } } + """ + + Scenario: Updating card metadata + Given I have tokenized a card + When I make a PATCH request to the href "href" with the body: + """ + [{ + "op": "add", + "path": "/cards/0/meta/asdf", + "value": "the value to be added" + }] + """ + + Then I fetch the card + And I make a PATCH request to the href "href" with the body: + """ + [{ + "op": "replace", + "path": "/cards/0/meta/asdf", + "value": "new value" + }] + """ + Then I should get a 200 OK status code + And the response is valid according to the "cards" schema + And the fields on this card match: + """ + { "meta": { "asdf": "new value" } } + """ + + Scenario: Safely updating the metadata + Given I have tokenized a card + When I make a PATCH request to the href "href" with the body: + """ + [{ + "op": "add", + "path": "/cards/0/meta/asdf", + "value": "the value to be tested" + }] + """ + + Then I fetch the card + And I make a PATCH request to the href "href" with the body: + """ + [{ + "op": "test", + "path": "/cards/0/meta/asdf", + "value": "the value to be tested" + },{ + "op": "replace", + "path": "/cards/0/meta/asdf", + "value": "after checking the value" + }] + """ + Then I should get a 200 OK status code + And the response is valid according to the "cards" schema + And the fields on this card match: + """ + { "meta": { "asdf": "after checking the value" } } + """ + + Scenario: Failing to safely update the metadata + Given I have tokenized a card + When I make a PATCH request to the href "href" with the body: + """ + [{ + "op": "add", + "path": "/cards/0/meta/asdf", + "value": "the value to be tested" + }] + """ + + Then I fetch the card + And I make a PATCH request to the href "href" with the body: + """ + [{ + "op": "test", + "path": "/cards/0/meta/asdf", + "value": "not the right value" + },{ + "op": "replace", + "path": "/cards/0/meta/asdf", + "value": "after checking the value" + }] + """ + Then I should get a 409 Conflict status code + And the response is valid according to the "errors" schema + + Scenario: Moving metadata + Given I have tokenized a card + When I make a PATCH request to the href "href" with the body: + """ + [{ + "op": "add", + "path": "/cards/0/meta/asdf", + "value": "the value to be moved" + }] + """ + + Then I fetch the card + And I make a PATCH request to the href "href" with the body: + """ + [{ + "op": "move", + "from": "/cards/0/meta/asdf", + "path": "/cards/0/meta/zxcv" + }] + """ + Then I should get a 200 OK status code + And the response is valid according to the "cards" schema + And the fields on this card match: + """ + { "meta": { "zxcv": "the value to be moved" } } + """ + + Scenario: Removing metadata + Given I have tokenized a card + When I make a PATCH request to the href "href" with the body: + """ + [{ + "op": "add", + "path": "/cards/0/meta/asdf", + "value": "the value to be moved" + }] + """ + + Then I fetch the card + And I make a PATCH request to the href "href" with the body: + """ + [{ + "op": "remove", + "path": "/cards/0/meta/asdf" + }] + """ + Then I should get a 200 OK status code + And the response is valid according to the "cards" schema + And the fields on this card match: + """ + { "meta": { } } + """ From e0ff12a1ff95c22271caa74ea56e8427940764eb Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Thu, 16 Jan 2014 00:42:47 +0000 Subject: [PATCH 131/154] New feature: fail to confirm a bank account verification --- features/rest/bank_account_verifications.feature | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/features/rest/bank_account_verifications.feature b/features/rest/bank_account_verifications.feature index 682a31e..10c8656 100644 --- a/features/rest/bank_account_verifications.feature +++ b/features/rest/bank_account_verifications.feature @@ -37,3 +37,15 @@ Feature: Bank account verifications "verification_status": "succeeded" } """ + + Scenario: Fail to confirm a verification + Given I have a bank account with a verification + When I PUT to /verifications/:bank_account_verification_id with the JSON API body: + """ + { + "amount_1": 2, + "amount_2": 2 + } + """ + Then I should get a 409 Conflict status code + And the response is valid according to the "errors" schema From 2044e734f9fa8f95e7b151d05531e4898ddb6b86 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Thu, 16 Jan 2014 00:53:33 +0000 Subject: [PATCH 132/154] progress on 'inferring bank names' --- features/rest/bank_accounts.feature | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/features/rest/bank_accounts.feature b/features/rest/bank_accounts.feature index f7bd652..5b9d4a9 100644 --- a/features/rest/bank_accounts.feature +++ b/features/rest/bank_accounts.feature @@ -75,3 +75,37 @@ Feature: Bank accounts """ Then I should get a 201 Created status code And the response is valid according to the "debits" schema + + @failing @gh-449 + Scenario: Infer bank names + When I POST to /bank_accounts with the JSON API body: + """ + { + "name": "Michael Johnson", + "account_number": "982379283", + "routing_number": "121000358", + "account_type": "checking" + } + """ + Then I should get a 201 Created status code + And the response is valid according to the "bank_accounts" schema + And the fields on this bank account match: + """ + {"bank_name": "BANK OF AMERICA, N.A."} + """ + + When I POST to /bank_accounts with the JSON API body: + """ + { + "name": "Maurice Green", + "account_number": "33727930", + "routing_number": "322271627", + "account_type": "checking" + } + """ + Then I should get a 201 Created status code + And the response is valid according to the "bank_accounts" schema + And the fields on this bank account match: + """ + {"bank_name": "J.P. MORGAN CHASE BANK, N.A."} + """ From 9513dd2c15a896cfb9d069b84fd6582174535527 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Thu, 16 Jan 2014 01:02:37 +0000 Subject: [PATCH 133/154] Make progress on 'add a bank account to a customer' --- features/rest/bank_accounts.feature | 19 +++++++++++++++++++ features/step_definitions/http_steps.rb | 1 + 2 files changed, 20 insertions(+) diff --git a/features/rest/bank_accounts.feature b/features/rest/bank_accounts.feature index 5b9d4a9..ef2a205 100644 --- a/features/rest/bank_accounts.feature +++ b/features/rest/bank_accounts.feature @@ -109,3 +109,22 @@ Feature: Bank accounts """ {"bank_name": "J.P. MORGAN CHASE BANK, N.A."} """ + + @failing @gh-449 + Scenario: Add a bank account to a customer + Given I have created a customer + And I have tokenized a bank account + When I make a PATCH request to the href "href" with the body: + """ + [{ + "op": "replace", + "path": "/bank_accounts/0/links/customer", + "value": "<%= @customer_id %>" + }] + """ + Then I should get a 200 OK status code + And the response is valid according to the "bank_accounts" schema + And the fields on this bank account match: + """ + { "links": { "customer": "<%= @customer_id %>" } } + """ diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 7dadfae..92b0209 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -48,6 +48,7 @@ def env end When(/^I make a (\w+) request to the href "(.*?)" with the body:$/) do |verb, keys, body| + body = ERB.new(body).result(binding) link = @client[keys] || @client.inject(keys) @client.send(verb.downcase, link, body, env) end From 9ba89de58b883c645adf701dcda09ce7dfd255e1 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Wed, 15 Jan 2014 18:20:40 -0800 Subject: [PATCH 134/154] removing failing tag from card scenarios --- features/credit_cards.feature | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index 14db03e..ad76ecd 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -1,22 +1,23 @@ Feature: Credit cards - @failing @gh-479 Scenario: Add a card to a customer - Given I have created a customer - When I make a PATCH request to the href "href" with the body: + Given I have tokenized a card + And I have created a customer + When I make a PATCH request to /cards/:card_id with the body: """ [{ "op": "replace", "path": "/cards/0/links/customer", - "value": "<%= @customer_id %>" + "value": ":customer_id" }] """ Then I should get a 200 OK status code - And the response is valid according to the "cards" schema + And I make a GET request to /cards/:card_id + Then the response is valid according to the "cards" schema And the fields on this card match: """ { - "links": { "customer": "<%= @customer_id %>" } + "links": { "customer": ":customer_id" } } """ @@ -86,7 +87,6 @@ Feature: Credit cards } """ - @failing @gh-438 Scenario: AVS Postal code is unused When I make a POST request to /cards with the body: """ @@ -152,7 +152,6 @@ Feature: Credit cards } """ - @failing @gh-438 Scenario: AVS street match is null When I make a POST request to /cards with the body: """ @@ -172,7 +171,6 @@ Feature: Credit cards } """ - @failing @gh-438 Scenario: Detect a Visa card brand When I make a POST request to /cards with the body: """ @@ -192,7 +190,6 @@ Feature: Credit cards } """ - @failing @gh-438 Scenario: Detect a Mastercard card brand When I make a POST request to /cards with the body: """ @@ -212,7 +209,6 @@ Feature: Credit cards } """ - @failing @gh-438 Scenario: Detect an American Express card brand When I make a POST request to /cards with the body: """ @@ -232,7 +228,6 @@ Feature: Credit cards } """ - @failing @gh-438 Scenario: Detect a Discover card brand When I make a POST request to /cards with the body: """ @@ -252,7 +247,6 @@ Feature: Credit cards } """ - @failing @gh-480 Scenario: Retrieving a card Given I have tokenized a card When I make a GET request to /cards/:card_id @@ -271,7 +265,7 @@ Feature: Credit cards "address": { "line1": "965 Mission St", "line2": null, - "city": null, + "city": "Balo Alto", "state": null, "postal_code": "94103", "country_code": null @@ -284,7 +278,6 @@ Feature: Credit cards } """ - @failing @gh-438 Scenario: Tokenizing a card When I make a POST request to /cards with the body: """ @@ -353,7 +346,6 @@ Feature: Credit cards } """ - @failing @gh-481 Scenario: Tokenization fails luhn test When I make a POST request to /cards with the body: """ @@ -377,8 +369,6 @@ Feature: Credit cards When I make a DELETE request to /cards/:card_id Then I should get a 204 status code - - @failing @gh-438 Scenario: CVV matches When I make a POST request to /cards with the body: """ @@ -399,7 +389,6 @@ Feature: Credit cards } """ - @failing @gh-438 Scenario: CVV does not match When I make a POST request to /cards with the body: """ @@ -420,7 +409,6 @@ Feature: Credit cards } """ - @failing @gh-438 Scenario: CVV is unsupported When I make a POST request to /cards with the body: """ @@ -441,7 +429,6 @@ Feature: Credit cards } """ - @failing @gh-438 Scenario: CVV is unused When I make a POST request to /cards with the body: """ From 5f9f1c7ac7101d6473d273dca77d7343aa93ae8c Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Wed, 15 Jan 2014 19:09:12 -0800 Subject: [PATCH 135/154] cleaning orders features --- features/orders.feature | 34 +++++++++++++------------ features/step_definitions/customers.rb | 7 ++++- features/step_definitions/http_steps.rb | 1 + 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/features/orders.feature b/features/orders.feature index 2767105..396471c 100644 --- a/features/orders.feature +++ b/features/orders.feature @@ -7,7 +7,6 @@ Feature: Orders Then I should get a 201 Created status code And the response is valid according to the "orders" schema - @failing @gh-469 Scenario: Basic order flow Given I have a customer with a tokenized bank account When I make a POST request to /customers/:customer_id/orders @@ -16,7 +15,7 @@ Feature: Orders And the fields on this order match: """ { - "links": { "merchant": "<%= @customer_id %>" } + "links": { "merchant": ":customer_id" } } """ @@ -25,7 +24,7 @@ Feature: Orders """ { "amount": 10000, - "order": "<%= @order_href %>" + "order": ":order_id" } """ @@ -35,13 +34,13 @@ Feature: Orders """ { "links": { - "order": "<%= @order_id %>" + "order": ":order_id" } } """ When I fetch the order - And I make a GET request to the link "orders.href" + And I make a GET request to /orders/:order_id Then I should get a 200 OK status code And the response is valid according to the "orders" schema @@ -58,7 +57,7 @@ Feature: Orders """ { "amount": 10000, - "order": "<%= @order_href %>" + "order": ":order_id" } """ @@ -68,13 +67,13 @@ Feature: Orders """ { "links": { - "order": "<%= @order_id %>" + "order": ":order_id" } } """ When I fetch the order - And I make a GET request to the link "orders.href" + And I make a GET request to /orders/:order_id Then I should get a 200 OK status code And the response is valid according to the "orders" schema @@ -86,7 +85,6 @@ Feature: Orders } """ - @failing @gh-469 Scenario: Checking escrow of order after creating a debit Given I have created an order And I have tokenized a customer card @@ -225,7 +223,7 @@ Feature: Orders """ - @failing @gh-471 @focusz + @failing @gh-471 Scenario: Create a failed refund when insufficient funds are in order escrow Given I have created an order And I have tokenized a card @@ -257,9 +255,8 @@ Feature: Orders @failing @gh-469 Scenario: Transactions should inherit the description of the order by default - Given I have created an order - And I have tokenized a bank account - When I fetch the customer + Given I have tokenized a bank account + And I have created a customer And I make a POST request to the link "customers.orders" with the body: """ { @@ -275,12 +272,14 @@ Feature: Orders "description": "Beats by Dr. Dre" } """ + Then debug - And I make a POST request to the link "customers.debits" with the body: + Given I have another customer with a card + Then I make a POST request to the link "customers.debits" with the body: """ { "amount": 10000, - "order": ":order_id", + "order": ":order_id" } """ @@ -293,11 +292,14 @@ Feature: Orders } """ + # this scenario becomes complicted, as there are two different customers + # that we need to reference. The first customer is the merchant which the order + # is created under. The second is the one doing the buying. And I make a POST request to the link "customers.credits" with the body: """ { "amount": 10000, - "order": ":order_id", + "order": ":order_id" } """ Then I should get a 201 Created status code diff --git a/features/step_definitions/customers.rb b/features/step_definitions/customers.rb index 3992b6d..1dcb3ad 100644 --- a/features/step_definitions/customers.rb +++ b/features/step_definitions/customers.rb @@ -1,5 +1,5 @@ Given(/^I have created a customer$/) do - @client.post('/customers', + @client.post('/customers', { name: 'Henry Ford', dob: '1863-07', @@ -85,3 +85,8 @@ response = HTTParty.delete("#{@client.root_url}/customers/#{@customer_id}", options) @client.add_response(response) end + + +Given(/^I have another customer with a card$/) do + step 'I have created a customer' +end diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 92b0209..698712f 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -60,6 +60,7 @@ def env @credit_id = @client['credits']['id'] rescue nil @cards_id = @client['cards']['id'] rescue nil @client.add_hydrate(:card_id, @cards_id) if @cards_id + @client.add_hydrate(:order_id, @client['orders']['id']) if @client['orders'] @debit_url = @client['debits']['href'] rescue nil body end From 13925648e741f2fb486d5fce20753c4ca372c388 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Thu, 16 Jan 2014 12:16:50 -0800 Subject: [PATCH 136/154] trying new link resolve system --- Gemfile | 1 + Gemfile.lock | 8 +++++++ features/checkout_flow.feature | 32 ++++++++++++++----------- features/step_definitions/http_steps.rb | 6 +++-- lib/balanced/tiny_client.rb | 18 ++++++++++++++ 5 files changed, 49 insertions(+), 16 deletions(-) diff --git a/Gemfile b/Gemfile index 162433d..42a4a6b 100644 --- a/Gemfile +++ b/Gemfile @@ -5,3 +5,4 @@ gem "httparty" gem "json-schema" gem "uri_template" gem "rake" +gem 'debugger' diff --git a/Gemfile.lock b/Gemfile.lock index c073ad4..e050348 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,12 +2,19 @@ GEM remote: https://rubygems.org/ specs: builder (3.2.2) + columnize (0.3.6) cucumber (1.3.10) builder (>= 2.1.2) diff-lcs (>= 1.1.3) gherkin (~> 2.12) multi_json (>= 1.7.5, < 2.0) multi_test (>= 0.0.2) + debugger (1.6.5) + columnize (>= 0.3.1) + debugger-linecache (~> 1.2.0) + debugger-ruby_core_source (~> 1.3.1) + debugger-linecache (1.2.0) + debugger-ruby_core_source (1.3.1) diff-lcs (1.2.5) gherkin (2.12.2) multi_json (~> 1.3) @@ -27,6 +34,7 @@ PLATFORMS DEPENDENCIES cucumber + debugger httparty json-schema rake diff --git a/features/checkout_flow.feature b/features/checkout_flow.feature index 2f93e37..f180e33 100644 --- a/features/checkout_flow.feature +++ b/features/checkout_flow.feature @@ -2,9 +2,16 @@ Feature: Credit cards @failing @gh-469 Scenario: Canceling an order - Given I have created an order - Then I make a GET request to /orders/:order_id - Then I should get a 200 OK status code + # This does not copy all the steps as this depended on a basic checkout + # flow scenario with a number of steps + Given I have created a customer + When I POST to /customers/:customer_id/orders with the JSON API body: + """ + { + "description": "Catherine Malandrino Black Top" + } + """ + Then I should get a 201 Created status code And the response is valid according to the "orders" schema # how is this going to match, as we have not set any values on the order # so the description is going to be empty @@ -50,7 +57,6 @@ Feature: Credit cards } """ - @failing @gh-469 Scenario: Existing buyer makes a purchase with a new card Given I have created a customer When I make a GET request to /customers/:customer_id @@ -93,7 +99,6 @@ Feature: Credit cards } """ - And debug When I PATCH to /customers/:customer_id with the JSON API body: """ [{ @@ -111,7 +116,7 @@ Feature: Credit cards } """ - When I make a POST request to /customers/:customer_id/orders with the body: + When I make a POST request to the link "customers.orders" with the body: """ { "description": "Catherine Malandrino Black Top", @@ -136,7 +141,7 @@ Feature: Credit cards } """ - When I make a POST request to the link "customers.debits" with the body: + When I make a POST request to /customers/:customer_id/debits with the body: """ { "amount": 10000, @@ -153,7 +158,7 @@ Feature: Credit cards "appears_on_statement_as": "BAL*Vaunte-Alice Ryan", "status": "succeeded", "links": { - "order": "<%= @orders_id %>", + "order": ":order_id" } } """ @@ -181,7 +186,6 @@ Feature: Credit cards } """ - @failing @gh-469 Scenario: Existing buyer makes a purchase with an existing card Given I have created a customer When I make a GET request to /customers/:customer_id @@ -213,7 +217,7 @@ Feature: Credit cards And the fields on this order match: """ { - "links":{ "merchant": "<%= @customers_id %>" } + "links":{ "merchant": ":customer_id" } } """ @@ -221,7 +225,7 @@ Feature: Credit cards """ { "amount": 10000, - "order": "<%= @orders_id %>", + "order": ":order_id", "appears_on_statement_as": "Vaunte-Alice Ryan" } """ @@ -234,7 +238,7 @@ Feature: Credit cards "appears_on_statement_as": "BAL*Vaunte-Alice Ryan", "status": "succeeded", "links": { - "order": "<%= @orders_id %>", + "order": ":order_id" } } """ @@ -349,7 +353,7 @@ Feature: Credit cards """ { "amount": 10000, - "order": "<%= @orders_id %>", + "order": ":order_id", "appears_on_statement_as": "Vaunte-Alice Ryan" } """ @@ -362,7 +366,7 @@ Feature: Credit cards "appears_on_statement_as": "BAL*Vaunte-Alice Ryan", "status": "succeeded", "links": { - "order": "#{@orders_id}", + "order": ":order_id", } } """ diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 698712f..92a25d4 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -19,6 +19,7 @@ @client.verb(verb, @client.hydrater(url), env, body) @customer_id = @client['customers']['id'] rescue nil @client.add_hydrate(:customer_id, @customer_id) if @customer_id + @client.add_hydrate(:order_id, @client['orders']['id']) if @client['orders']['id'] end When(/^I make a (\w+) request to (\/\S*?)$/) do |verb, url| @@ -55,8 +56,9 @@ def env When(/^I make a (\w+) request to the link "(.*?)" with the body:$/) do |verb, keys, body| body = ERB.new(@client.hydrater(body)).result(binding) - $logger.debug("Requesting hydrated: #{@client.hydrater(@client.last_body["links"][keys])}") - body = @client.send(verb.downcase, @client.hydrater(@client.last_body["links"][keys]), JSON.parse(body), env) + href = @client.get_link(keys) + #$logger.debug("Requesting hydrated: #{@client.hydrater(@client.last_body["links"][keys])}") + body = @client.send(verb.downcase, @client.get_link(keys), JSON.parse(body), env) @credit_id = @client['credits']['id'] rescue nil @cards_id = @client['cards']['id'] rescue nil @client.add_hydrate(:card_id, @cards_id) if @cards_id diff --git a/lib/balanced/tiny_client.rb b/lib/balanced/tiny_client.rb index 45bcf54..b932f69 100644 --- a/lib/balanced/tiny_client.rb +++ b/lib/balanced/tiny_client.rb @@ -131,6 +131,24 @@ def last_body end end + def get_link(keys) + @responses.reverse.each do |response| + #require 'debugger'; debugger + body= JSON.parse(response.body) + if body['links'][keys] + key = body['links'][keys] + kk = key.gsub(/\{(\w+)\.(\w+)\}/) do |match| + a = match[1...-1].split('.') + body[a[0]][0][a[1]] + end + #require 'debugger'; debugger + return kk + #return keys.split('.').inject(body)json['links'][keys] + end + end + '/boom' + end + def inject(key) # hax to access a Ruby hash like dot notation key.split('.').inject(last_body) {|o, k| Array(o[k])[0] } From e4bc413ab6f98c8338c9149648b17e60bf1974a7 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Thu, 16 Jan 2014 12:22:33 -0800 Subject: [PATCH 137/154] fix --- features/step_definitions/http_steps.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 92a25d4..3888302 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -19,7 +19,7 @@ @client.verb(verb, @client.hydrater(url), env, body) @customer_id = @client['customers']['id'] rescue nil @client.add_hydrate(:customer_id, @customer_id) if @customer_id - @client.add_hydrate(:order_id, @client['orders']['id']) if @client['orders']['id'] + @client.add_hydrate(:order_id, @client['orders']['id']) rescue nil end When(/^I make a (\w+) request to (\/\S*?)$/) do |verb, url| From 2eec38b901e2b9b3db7e1fe904ab79015aca6554 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Thu, 16 Jan 2014 13:40:09 -0800 Subject: [PATCH 138/154] another one working --- features/checkout_flow.feature | 13 ++++--------- features/step_definitions/http_steps.rb | 2 +- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/features/checkout_flow.feature b/features/checkout_flow.feature index f180e33..3bd57c3 100644 --- a/features/checkout_flow.feature +++ b/features/checkout_flow.feature @@ -266,7 +266,6 @@ Feature: Credit cards } """ - @failing @gh-469 Scenario: New buyer makes a purchase When I POST to /cards with the JSON API body: """ @@ -287,10 +286,6 @@ Feature: Credit cards """ Then I should get a 201 CREATED status code And the response is valid according to the "cards" schema - - When I make a GET request to /cards/:cards_id - Then I should get a 200 OK status code - And the response is valid according to the "cards" schema And the fields on this card match: """ { @@ -305,7 +300,7 @@ Feature: Credit cards { "name": "Darius the Great", "email": "darius.great@gmail.com", - "source": "<%= @card_id %>", + "source": ":card_id", "meta": { "ip_address": "174.240.15.249" } @@ -318,7 +313,7 @@ Feature: Credit cards """ { "links": { - "source": "<%= @card_id %>", + "source": ":card_id", "destination": null } } @@ -345,7 +340,7 @@ Feature: Credit cards And the fields on this order match: """ { - "links":{ "merchant": "<%= @customers_id %>" } + "links":{ "merchant": ":customer_id" } } """ @@ -366,7 +361,7 @@ Feature: Credit cards "appears_on_statement_as": "BAL*Vaunte-Alice Ryan", "status": "succeeded", "links": { - "order": ":order_id", + "order": ":order_id" } } """ diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 3888302..bcb3493 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -121,7 +121,7 @@ def env body = @client.post(@client.hydrater(url), @client.hydrater(body)) @credit_id = @client['credits']['id'] rescue nil @card_id = @client['cards']['id'] rescue nil - @client.add_hydrate(:cards_id, @card_id) if @card_id + @client.add_hydrate(:card_id, @card_id) if @card_id body end From 27706466251e5e1b5f320b1b0eac12fa3b84e240 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Thu, 16 Jan 2014 13:50:35 -0800 Subject: [PATCH 139/154] fixing bank account rest scenarios --- features/rest/bank_accounts.feature | 16 +++++++++------- features/step_definitions/http_steps.rb | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/features/rest/bank_accounts.feature b/features/rest/bank_accounts.feature index ef2a205..b245967 100644 --- a/features/rest/bank_accounts.feature +++ b/features/rest/bank_accounts.feature @@ -76,7 +76,6 @@ Feature: Bank accounts Then I should get a 201 Created status code And the response is valid according to the "debits" schema - @failing @gh-449 Scenario: Infer bank names When I POST to /bank_accounts with the JSON API body: """ @@ -89,7 +88,7 @@ Feature: Bank accounts """ Then I should get a 201 Created status code And the response is valid according to the "bank_accounts" schema - And the fields on this bank account match: + And the fields on this bank_account match: """ {"bank_name": "BANK OF AMERICA, N.A."} """ @@ -105,12 +104,11 @@ Feature: Bank accounts """ Then I should get a 201 Created status code And the response is valid according to the "bank_accounts" schema - And the fields on this bank account match: + And the fields on this bank_account match: """ {"bank_name": "J.P. MORGAN CHASE BANK, N.A."} """ - @failing @gh-449 Scenario: Add a bank account to a customer Given I have created a customer And I have tokenized a bank account @@ -119,12 +117,16 @@ Feature: Bank accounts [{ "op": "replace", "path": "/bank_accounts/0/links/customer", - "value": "<%= @customer_id %>" + "value": ":customer_id" }] """ Then I should get a 200 OK status code And the response is valid according to the "bank_accounts" schema - And the fields on this bank account match: + And the fields on this bank_account match: """ - { "links": { "customer": "<%= @customer_id %>" } } + { + "links": { + "customer": ":customer_id" + } + } """ diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index bcb3493..78ab92f 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -49,7 +49,7 @@ def env end When(/^I make a (\w+) request to the href "(.*?)" with the body:$/) do |verb, keys, body| - body = ERB.new(body).result(binding) + body = ERB.new(@client.hydrater body).result(binding) link = @client[keys] || @client.inject(keys) @client.send(verb.downcase, link, body, env) end From 7d774b5d9115765e3707d4cd6ea906b0fad5b161 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Thu, 16 Jan 2014 15:30:02 -0800 Subject: [PATCH 140/154] fixed 404 error --- features/credits.feature | 3 --- 1 file changed, 3 deletions(-) diff --git a/features/credits.feature b/features/credits.feature index 4db4f13..8456b55 100644 --- a/features/credits.feature +++ b/features/credits.feature @@ -1,7 +1,6 @@ Feature: Credits Credits are used for sending money to a customer - @failing @gh-467 Scenario: Crediting a deleted card leads to failure Given I have tokenized a card When I make a DELETE request to the href "href" @@ -10,8 +9,6 @@ Feature: Credits When I make a POST request to /cards/:cards_id/credits Then I should get a 404 status code - # the 404 error comes back in the error schema from rev0 instead of rev1 - And the response is valid according to the "errors" schema And the fields on this error match: """ From f802d28d2f7b82e4f6bf03f79adf47a908982ec1 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Thu, 16 Jan 2014 16:03:53 -0800 Subject: [PATCH 141/154] canceling an order --- features/checkout_flow.feature | 59 +++++------------------------ features/step_definitions/orders.rb | 13 +++++++ 2 files changed, 22 insertions(+), 50 deletions(-) diff --git a/features/checkout_flow.feature b/features/checkout_flow.feature index 3bd57c3..7d54335 100644 --- a/features/checkout_flow.feature +++ b/features/checkout_flow.feature @@ -1,61 +1,20 @@ Feature: Credit cards - @failing @gh-469 Scenario: Canceling an order - # This does not copy all the steps as this depended on a basic checkout - # flow scenario with a number of steps - Given I have created a customer - When I POST to /customers/:customer_id/orders with the JSON API body: - """ - { - "description": "Catherine Malandrino Black Top" - } - """ - Then I should get a 201 Created status code - And the response is valid according to the "orders" schema - # how is this going to match, as we have not set any values on the order - # so the description is going to be empty - And the fields on this order match: - """ - { - "description": "Catherine Malandrino Black Top" - } - """ - - Then I make a GET request to the link "orders.debits" - Then I should get a 200 OK status code - And the response is valid according to the "debits" schema - And the fields on this debit match: - """ - { - "amount": 10000, - "description": "Catherine Malandrino Black Top" - } - """ - - Then I make a POST request to the link "debits.refunds" - Then I should get a 201 OK status code + Given I have an order with a debit + When I POST to /debits/:debit_id/refunds + Then I should get a 201 CREATED status code And the response is valid according to the "refunds" schema - And the fields on this refund match: - """ - { - "amount": 10000, - "description": "Catherine Malandrino Black Top" - } - """ Then I make a GET request to /orders/:order_id - Then I should get a 200 OK status code And the response is valid according to the "orders" schema And the fields on this order match: - """ - { - "amount": 0, - "amount_escrowed": 0, - "currency": "USD", - "description": "Catherine Malandrino Black Top" - } - """ + """ + { + "amount": 0, + "amount_escrowed": 0 + } + """ Scenario: Existing buyer makes a purchase with a new card Given I have created a customer diff --git a/features/step_definitions/orders.rb b/features/step_definitions/orders.rb index c836adf..66c070f 100644 --- a/features/step_definitions/orders.rb +++ b/features/step_definitions/orders.rb @@ -3,3 +3,16 @@ @client.post(@client.hydrater('/customers/:customer_id/orders'), {}) @client.add_hydrate :order_id, @client['id'] end + +Given(/^I have an order with a debit$/) do + step 'I have created a customer' + merchant = @client.post('/customers', {}) + @client.post("/customers/#{@client['id']}/orders", {}) + @order_id = @client['id'] + @client.add_hydrate :order_id, @order_id + @client.post("/cards/#{@card_id}/debits", { + amount: 12345, + order: @order_id + }) + @client.add_hydrate :debit_id, @client['id'] +end From 4b13e4376e09aff96051fb59680ca015cb3c0849 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Thu, 16 Jan 2014 17:27:04 -0800 Subject: [PATCH 142/154] the end is nearing --- features/orders.feature | 21 +++++++-------------- features/step_definitions/http_steps.rb | 4 ++-- features/step_definitions/orders.rb | 13 +++++++++++++ lib/balanced/tiny_client.rb | 4 ++-- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/features/orders.feature b/features/orders.feature index 396471c..4af69c6 100644 --- a/features/orders.feature +++ b/features/orders.feature @@ -106,20 +106,14 @@ Feature: Orders } """ - @failing Scenario: Checking escrow of order after creating a credit - # this scenario makes no since as you are attempting to credit - # out from an order when there is no money in the order itself - # I believe that you were attempting to copy a scenario - # that was adding money to the order first - Given I have created an order - And I have sufficient funds in my marketplace + Given I have an order with a debit And I have tokenized a bank account And I POST to /bank_accounts/:bank_account_id/credits with the JSON API body: """ { "order": ":order_id", - "amount": 1234 + "amount": 12345 } """ When I make a GET request to /orders/:order_id @@ -128,7 +122,8 @@ Feature: Orders And the fields on this order match: """ { - "amount_escrowed": 1234 + "amount": 12345, + "amount_escrowed": 0 } """ @@ -222,8 +217,6 @@ Feature: Orders } """ - - @failing @gh-471 Scenario: Create a failed refund when insufficient funds are in order escrow Given I have created an order And I have tokenized a card @@ -234,7 +227,7 @@ Feature: Orders "amount": 1234 } """ - And I have tokenized a bank account + And I have tokenized a bank account and associated with the merchant Then I make a POST request to the link "bank_accounts.credits" with the body: """ { @@ -243,13 +236,13 @@ Feature: Orders } """ - When I make a POST request to the link "debits.refunds" of that debit + When I make a POST request to the link "debits.refunds" Then I should get a 409 status code And the response is valid according to the "errors" schema And the fields on this error match: """ { - "category_code": "account-insufficient-funds" + "category_code": "insufficient-funds" } """ diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index 78ab92f..c1e95d8 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -80,8 +80,8 @@ def env end When(/^I make a (\w+) request to the link "(.*?)"$/) do |verb, keys| - $logger.debug("Requesting hydrated: #{@client.hydrater(@client.last_body["links"][keys])}") - body = @client.send(verb.downcase, @client.hydrater(@client.last_body["links"][keys]), {}, env) + #$logger.debug("Requesting hydrated: #{@client.hydrater(@client.last_body["links"][keys])}") + body = @client.send(verb.downcase, @client.get_link(keys), {}, env) @cards_id = @client['cards']['id'] rescue nil @client.add_hydrate(:card_id, @cards_id) if @cards_id @credit_id = @client['credits']['id'] rescue nil diff --git a/features/step_definitions/orders.rb b/features/step_definitions/orders.rb index 66c070f..896d4b2 100644 --- a/features/step_definitions/orders.rb +++ b/features/step_definitions/orders.rb @@ -1,5 +1,7 @@ Given(/^I have created an order$/) do step 'I have created a customer' + @merchant_id = @customer_id + @client.add_hydrate :merchant_id, @merchant_id @client.post(@client.hydrater('/customers/:customer_id/orders'), {}) @client.add_hydrate :order_id, @client['id'] end @@ -7,6 +9,7 @@ Given(/^I have an order with a debit$/) do step 'I have created a customer' merchant = @client.post('/customers', {}) + @client.add_hydrate :merchant_id, @client['id'] @client.post("/customers/#{@client['id']}/orders", {}) @order_id = @client['id'] @client.add_hydrate :order_id, @order_id @@ -16,3 +19,13 @@ }) @client.add_hydrate :debit_id, @client['id'] end + + +When(/^I have tokenized a bank account and associated with the merchant$/) do + step 'I have tokenized a bank account' + @client.put("/bank_accounts/#{@bank_account_id}", { + links: { + customer: @merchant_id + } + }) +end diff --git a/lib/balanced/tiny_client.rb b/lib/balanced/tiny_client.rb index b932f69..1e173d8 100644 --- a/lib/balanced/tiny_client.rb +++ b/lib/balanced/tiny_client.rb @@ -134,8 +134,8 @@ def last_body def get_link(keys) @responses.reverse.each do |response| #require 'debugger'; debugger - body= JSON.parse(response.body) - if body['links'][keys] + body = JSON.parse(response.body) + if body and body['links'] and body['links'][keys] key = body['links'][keys] kk = key.gsub(/\{(\w+)\.(\w+)\}/) do |match| a = match[1...-1].split('.') From 5a1a65f77d6e8c606987e6bc9522f741bbb89d8f Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Thu, 16 Jan 2014 18:40:04 -0800 Subject: [PATCH 143/154] one more to go --- features/orders.feature | 50 ++++--------------------- features/step_definitions/http_steps.rb | 1 + features/step_definitions/orders.rb | 10 +++++ 3 files changed, 18 insertions(+), 43 deletions(-) diff --git a/features/orders.feature b/features/orders.feature index 4af69c6..19fc0d5 100644 --- a/features/orders.feature +++ b/features/orders.feature @@ -179,7 +179,7 @@ Feature: Orders } """ - @failing + @failing @focus Scenario: Create a reversal Given I have created an order And I have tokenized a customer card @@ -246,15 +246,12 @@ Feature: Orders } """ - @failing @gh-469 Scenario: Transactions should inherit the description of the order by default - Given I have tokenized a bank account - And I have created a customer - And I make a POST request to the link "customers.orders" with the body: + Given I have a merchant with an order with the body: """ - { - "description": "Beats by Dr. Dre" - } + { + "description": "Beats by Dr. Dre" + } """ Then I should get a 201 Created status code @@ -265,7 +262,6 @@ Feature: Orders "description": "Beats by Dr. Dre" } """ - Then debug Given I have another customer with a card Then I make a POST request to the link "customers.debits" with the body: @@ -288,6 +284,7 @@ Feature: Orders # this scenario becomes complicted, as there are two different customers # that we need to reference. The first customer is the merchant which the order # is created under. The second is the one doing the buying. + Then I make a GET request to /customers/:merchant_id And I make a POST request to the link "customers.credits" with the body: """ { @@ -300,39 +297,6 @@ Feature: Orders And the fields on this credit match: """ { - { "description": "Beats by Dr. Dre"} - } - """ - - @failing @gh-474 - Scenario: Crediting an unverified merchant will result in failure - Given I have created a non-underwritten customer with a tokenized bank account - And I make a POST request to the link "customers.orders" - - And I fetch the card - And I make a POST request to the link "cards.debits" with the body: - """ - { - "order": ":order_id", - "amount": 1234 - } - """ - Then I should get a 201 Created status code - - When I fetch the bank account - And I make a POST request to the link "bank_accounts.credits" with the body: - """ - { - "amount": 1234, - "order": ":order_id" - } - """ - Then I should get a 409 status code - And the response is valid according to the "errors" schema - And the fields on this error match: - """ - { - "description": "Order requires that merchant CU[a-zA-Z0-9]{16,32} be underwritten.", - "category_code": "order-kyc" + "description": "Beats by Dr. Dre" } """ diff --git a/features/step_definitions/http_steps.rb b/features/step_definitions/http_steps.rb index c1e95d8..80e4ade 100644 --- a/features/step_definitions/http_steps.rb +++ b/features/step_definitions/http_steps.rb @@ -85,6 +85,7 @@ def env @cards_id = @client['cards']['id'] rescue nil @client.add_hydrate(:card_id, @cards_id) if @cards_id @credit_id = @client['credits']['id'] rescue nil + @client.add_hydrate(:order_id, @client['orders']['id']) rescue nil body end diff --git a/features/step_definitions/orders.rb b/features/step_definitions/orders.rb index 896d4b2..fcfa93a 100644 --- a/features/step_definitions/orders.rb +++ b/features/step_definitions/orders.rb @@ -29,3 +29,13 @@ } }) end + + +Given(/^I have a merchant with an order with the body:$/) do |body| + step 'I have created a customer' + @merchant_id = @customer_id + @client.add_hydrate :merchant_id, @merchant_id + step 'I have tokenized a bank account and associated with the merchant' + @client.post("/customers/#{@merchant_id}/orders", @client.hydrater(body)) + @client.add_hydrate :order_id, @client['id'] +end From bf659a8d435bfbb92b9501bfbda30cde1337f550 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Thu, 16 Jan 2014 19:55:02 -0800 Subject: [PATCH 144/154] I did it, its done, no more failing --- features/orders.feature | 37 ++++++++++++++++------------- features/step_definitions/orders.rb | 3 ++- lib/balanced/tiny_client.rb | 2 +- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/features/orders.feature b/features/orders.feature index 19fc0d5..8415ec1 100644 --- a/features/orders.feature +++ b/features/orders.feature @@ -108,7 +108,7 @@ Feature: Orders Scenario: Checking escrow of order after creating a credit Given I have an order with a debit - And I have tokenized a bank account + And I have tokenized a bank account and associated with the merchant And I POST to /bank_accounts/:bank_account_id/credits with the JSON API body: """ { @@ -129,7 +129,7 @@ Feature: Orders Scenario: Orders cannot be credited more than escrow balance Given I have created an order - And I have tokenized a bank account + And I have tokenized a bank account and associated with the merchant And I have tokenized a customer card And I make a POST request to the link "cards.debits" with the body: """ @@ -179,27 +179,30 @@ Feature: Orders } """ - @failing @focus Scenario: Create a reversal - Given I have created an order - And I have tokenized a customer card - And I make a POST request to /customers/:customer_id/orders with the body: + Given I have an order with a debit + And I make a POST request to /customers/:merchant_id/bank_accounts with the body: """ - { - "order": ":order_id", - "amount": 1234 - } + { + "name": "Michael Jordan", + "account_number": "9900000002", + "routing_number": "021000021", + "account_type": "checking" + } """ - # this bank account does not automatically succeed, which means that the - # credits can not be reversed instantly - # this is how it would normally happen in a prod marketplace - # but there are some ba numbers that can be reversed - And I have tokenized a bank account And I make a POST request to the link "bank_accounts.credits" with the body: """ { "order": ":order_id", - "amount": 1234 + "amount": 12345 + } + """ + When I make a GET request to /orders/:order_id + And the response is valid according to the "orders" schema + And the fields on this order match: + """ + { + "amount_escrowed": 0 } """ @@ -213,7 +216,7 @@ Feature: Orders And the fields on this order match: """ { - "amount_escrowed": 0 + "amount_escrowed": 12345 } """ diff --git a/features/step_definitions/orders.rb b/features/step_definitions/orders.rb index fcfa93a..30e2b33 100644 --- a/features/step_definitions/orders.rb +++ b/features/step_definitions/orders.rb @@ -8,7 +8,8 @@ Given(/^I have an order with a debit$/) do step 'I have created a customer' - merchant = @client.post('/customers', {}) + @client.post('/customers', {}) + @merchant_id = @client['id'] @client.add_hydrate :merchant_id, @client['id'] @client.post("/customers/#{@client['id']}/orders", {}) @order_id = @client['id'] diff --git a/lib/balanced/tiny_client.rb b/lib/balanced/tiny_client.rb index 1e173d8..74a1ded 100644 --- a/lib/balanced/tiny_client.rb +++ b/lib/balanced/tiny_client.rb @@ -139,7 +139,7 @@ def get_link(keys) key = body['links'][keys] kk = key.gsub(/\{(\w+)\.(\w+)\}/) do |match| a = match[1...-1].split('.') - body[a[0]][0][a[1]] + body[a[0]][0][a[1]] or body[a[0]][0]['links'][a[1]] end #require 'debugger'; debugger return kk From df27867d58e02c3bd03fc308465f2abe0afb7a86 Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Fri, 17 Jan 2014 19:45:20 -0800 Subject: [PATCH 145/154] some issues with test credit card numbers --- features/credit_cards.feature | 6 +++++- features/credits.feature | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index ad76ecd..646bb1d 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -346,6 +346,7 @@ Feature: Credit cards } """ + @failing Scenario: Tokenization fails luhn test When I make a POST request to /cards with the body: """ @@ -355,6 +356,7 @@ Feature: Credit cards "expiration_year": 2016 } """ + # the api returns a 400, while running it locally returns a 409 Then I should get a 409 status code And the response is valid according to the "errors" schema And the fields on this error match: @@ -389,6 +391,7 @@ Feature: Credit cards } """ + @failing Scenario: CVV does not match When I make a POST request to /cards with the body: """ @@ -396,7 +399,7 @@ Feature: Credit cards "number": "4111111111111111", "expiration_month": 12, "expiration_year": 2016, - "cvv": "902" + "cvv": "200" } """ @@ -409,6 +412,7 @@ Feature: Credit cards } """ + @failing Scenario: CVV is unsupported When I make a POST request to /cards with the body: """ diff --git a/features/credits.feature b/features/credits.feature index 8456b55..4ae6975 100644 --- a/features/credits.feature +++ b/features/credits.feature @@ -6,7 +6,7 @@ Feature: Credits When I make a DELETE request to the href "href" Then I should get a 204 status code - When I make a POST request to /cards/:cards_id/credits + When I make a POST request to /cards/:card_id/credits Then I should get a 404 status code And the response is valid according to the "errors" schema From 79fd4dfb38fd646fda964a9dcd3b865c29de85ab Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 20 Jan 2014 19:19:30 +0000 Subject: [PATCH 146/154] properly tag luhn test failure. Should be linked to #481 --- features/credit_cards.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index 646bb1d..08ee62c 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -346,7 +346,7 @@ Feature: Credit cards } """ - @failing + @failing @gh-481 Scenario: Tokenization fails luhn test When I make a POST request to /cards with the body: """ From 8027255c4b6f39cf0a712fd28957c1aa131d6764 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 20 Jan 2014 19:28:22 +0000 Subject: [PATCH 147/154] Tagging CVV is unsupported failure. #486 --- features/credit_cards.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index 08ee62c..5be2698 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -412,7 +412,7 @@ Feature: Credit cards } """ - @failing + @failing @gh-486 Scenario: CVV is unsupported When I make a POST request to /cards with the body: """ From a6fa221ff97331adcd1eec486fea23eb6d54cca4 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 20 Jan 2014 19:30:13 +0000 Subject: [PATCH 148/154] Properly tagging cvv does not match #487 --- features/credit_cards.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index 5be2698..32987c5 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -391,7 +391,7 @@ Feature: Credit cards } """ - @failing + @failing @gh-487 Scenario: CVV does not match When I make a POST request to /cards with the body: """ From 79702b8328adcd23bf7aa81c2d241ae8f7f7949e Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Mon, 20 Jan 2014 16:21:06 -0800 Subject: [PATCH 149/154] cvv card number scenarios working --- features/credit_cards.feature | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index 32987c5..270c33c 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -391,12 +391,11 @@ Feature: Credit cards } """ - @failing @gh-487 Scenario: CVV does not match When I make a POST request to /cards with the body: """ { - "number": "4111111111111111", + "number": "5112000200000002", "expiration_month": 12, "expiration_year": 2016, "cvv": "200" @@ -412,12 +411,11 @@ Feature: Credit cards } """ - @failing @gh-486 Scenario: CVV is unsupported When I make a POST request to /cards with the body: """ { - "number": "4111111111111111", + "number": "4457000300000007 ", "expiration_month": 12, "expiration_year": 2016, "cvv": "901" From f08a98d2d90644a4b92257b88ae1875423a506dd Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Thu, 23 Jan 2014 04:03:25 +0000 Subject: [PATCH 150/154] it is all working now --- features/credit_cards.feature | 1 - 1 file changed, 1 deletion(-) diff --git a/features/credit_cards.feature b/features/credit_cards.feature index 270c33c..85129af 100644 --- a/features/credit_cards.feature +++ b/features/credit_cards.feature @@ -346,7 +346,6 @@ Feature: Credit cards } """ - @failing @gh-481 Scenario: Tokenization fails luhn test When I make a POST request to /cards with the body: """ From ce58fea89be20a2dab7fb18425c3077ec3a9694d Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 24 Jan 2014 01:16:55 +0000 Subject: [PATCH 151/154] Add feature for debiting a card directly. --- features/debits.feature | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/features/debits.feature b/features/debits.feature index 9af114c..d00c9cd 100644 --- a/features/debits.feature +++ b/features/debits.feature @@ -61,3 +61,18 @@ Feature: Debit cards """ Then I should get a 409 Conflict status code And the response is valid according to the "debits" schema + + Scenario: Debiting a card directly + When I POST to /debits with the body: + """ + { + "amount": 1234, + "source": { + "number": "4111111111111111", + "expiration_year": "2018", + "expiration_month": 12 + } + } + """ + Then I should get a 201 Created status code + And the response is valid according to the "debits" schema From 3a87dc0bc4a417862b894392c8186bfbbede261a Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 24 Jan 2014 01:25:29 +0000 Subject: [PATCH 152/154] Fix up specs related to reversals. Accidentally had two successes, now we have one and a fail. --- features/reversals.feature | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/features/reversals.feature b/features/reversals.feature index 78281b1..4c1b15d 100644 --- a/features/reversals.feature +++ b/features/reversals.feature @@ -32,7 +32,7 @@ Feature: Reversals Then I should get a 400 status code And the response is valid according to the "errors" schema - Scenario: Reverse a bank account credit + Scenario: Reverse a bank account credit successfully When I POST to /credits with the JSON API body: """ @@ -52,8 +52,12 @@ Feature: Reversals When I make a POST request to the link "credits.reversals" Then I should get a 201 Created status code And the response is valid according to the "reversals" schema + And the fields on this reversal match: + """ + { "status": "succeeded" } + """ - Scenario: Reverse a bank account credit + Scenario: Reverse a bank account credit unsuccessfully When I POST to /credits with the JSON API body: """ @@ -61,7 +65,7 @@ Feature: Reversals "amount": 1234, "destination": { "name": "Michael Jordan", - "account_number": "9900000095", + "account_number": "9900000004", "routing_number": "021000021", "account_type": "checking" } @@ -71,5 +75,9 @@ Feature: Reversals And the response is valid according to the "credits" schema When I make a POST request to the link "credits.reversals" - Then I should get a 201 Created status code + Then I should get a 409 Conflict status code And the response is valid according to the "reversals" schema + And the fields on this reversal match: + """ + { "status": "failed" } + """ From 82540eb80079917b49708773d13e56975f1e6ddd Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 24 Jan 2014 01:35:14 +0000 Subject: [PATCH 153/154] New feature: update a customer --- features/rest/customers.feature | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/features/rest/customers.feature b/features/rest/customers.feature index 6e2c1e3..786efa9 100644 --- a/features/rest/customers.feature +++ b/features/rest/customers.feature @@ -39,6 +39,23 @@ Feature: Customers Then I should get a 204 OK status code And there should be no response body + Scenario: Update a customer + Given I have created a customer + When I make a PUT request to the href "href" with the body: + """ + { + "email": "asdf@balancedpayments.com" + } + """ + Then I should get a 200 OK status code + And the response is valid according to the "customers" schema + And the fields on this customer match: + """ + { + "email": "asdf@balancedpayments.com" + } + """ + Scenario: Add a card to a customer Given I have tokenized a card And I have created a customer From 2b9b0197eca3c354e7708a003665ddecaccddbc8 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 24 Jan 2014 01:49:20 +0000 Subject: [PATCH 154/154] Two last bank account features. --- features/rest/bank_accounts.feature | 43 +++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/features/rest/bank_accounts.feature b/features/rest/bank_accounts.feature index b245967..b4f49bf 100644 --- a/features/rest/bank_accounts.feature +++ b/features/rest/bank_accounts.feature @@ -22,6 +22,49 @@ Feature: Bank accounts Then I should get a 200 OK status code And the response is valid according to the "bank_accounts" schema + Scenario: Tokenize a savings account + When I make a POST request to /bank_accounts with the body: + """ + { + "name": "Jack Lalanne", + "account_number": "200938202", + "routing_number": "121042882", + "account_type": "savings" + } + """ + + Then I should get a 201 Created status code + And the response is valid according to the "bank_accounts" schema + And the fields on this bank_account match: + """ + { "account_type": "savings" } + """ + + Scenario: Tokenize a bank account with a country code + When I make a POST request to /bank_accounts with the body: + """ + { + "name": "Mahmoud Abdelkader", + "account_number": "200938202", + "routing_number": "121042882", + "account_type": "checking", + "address": { + "country_code": "US" + } + } + """ + + Then I should get a 201 Created status code + And the response is valid according to the "bank_accounts" schema + And the fields on this bank_account match: + """ + { + "address": { + "country_code": "US" + } + } + """ + Scenario: Retrieve a bank account Given I have tokenized a bank account When I GET to /bank_accounts/:bank_account_id