Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bulk credits sweep account #707

Merged
merged 39 commits into from
Dec 20, 2014
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
c8530e8
Add Account spec
remear Oct 6, 2014
29279bd
Add sweep_account link to BankAccount resource
remear Oct 6, 2014
8471295
Add bulk credit to sweep account feature
remear Oct 6, 2014
f42089a
Remove transfers link
remear Oct 6, 2014
172a527
Spec Account and Settlement resources. Update bulk credit feature.
remear Oct 16, 2014
306bee1
Create Settlements on POST to account settlements
remear Oct 20, 2014
6fe13fd
Add credits and reversals links to Settlements
remear Oct 20, 2014
a9dd0a9
Add reversals and refunds links to Accounts
remear Oct 20, 2014
18d6292
Add type to Account. Remove deposit_account link from Customer.
remear Oct 20, 2014
8045aba
Remove type from Account
remear Oct 21, 2014
0aeab75
Add can_debit and can_credit fields to Account
remear Oct 21, 2014
1e49b4d
POST settlements with no destination should 400
remear Oct 21, 2014
642370e
Use source instead
remear Oct 21, 2014
d3e31f8
More settlement scenarios. Cleanup.
remear Nov 13, 2014
e4d8cbf
Fix settlement reversals scenarios
remear Nov 13, 2014
745d043
Add settlement scenario account insufficient funds
remear Nov 13, 2014
2e3b457
More descriptive Accounts description
remear Nov 13, 2014
f2c7675
Fix bulk credit feature
rserna2010 Dec 5, 2014
8ef8470
Fix some errors
remear Dec 6, 2014
d1e4036
Edit create customer step
rserna2010 Dec 6, 2014
36352e7
Merge branch 'bulk-credits-sweep-account' of github.com:balanced/bala…
rserna2010 Dec 6, 2014
1365a5b
Fix syntax error in reversals, clean up settlements steps
rserna2010 Dec 6, 2014
9a36b43
Change categroy code for missing funding instrument to request
rserna2010 Dec 8, 2014
676ffec
Fix variable for credit_id_2
rserna2010 Dec 8, 2014
8e6280e
change back category code for credits, edit settlements instead
rserna2010 Dec 8, 2014
b617c04
Fix account step, missing with body:
rserna2010 Dec 8, 2014
2b66e22
remove extra bank account call on settlement feature
rserna2010 Dec 10, 2014
9b0f434
Fix gsub issue causing malformed order ids
rserna2010 Dec 11, 2014
e988cc9
Correct typo in accounts feature
rserna2010 Dec 11, 2014
ff15533
Edit settlemtent fixture to accept transaction_number, fix broken rev…
rserna2010 Dec 11, 2014
f59e0dc
Add new tests for accoutns
rserna2010 Dec 13, 2014
aa0cc6f
Add tests and fixtures for testing links for accounts and settlements
rserna2010 Dec 13, 2014
2ff55e8
fix id for reversals feature:
rserna2010 Dec 13, 2014
8b81e3e
Edit checker in http_steps to evaluate nested entitites, such as events
rserna2010 Dec 16, 2014
959a691
add tests for negtive balances in account, change settlement schema t…
rserna2010 Dec 19, 2014
f75e3a0
Chnage bank account used to always transition to succeed
rserna2010 Dec 19, 2014
b5538ed
fix status of bank account trasnactions
rserna2010 Dec 19, 2014
ec3af70
Fixes
remear Dec 20, 2014
08ad48e
Fix parse error
remear Dec 20, 2014
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions features/accounts.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
Feature: Accounts
Accounts are a way to have a store of some kind of value.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accounts are funding instruments which are able to store a balance internally with the Balanced system.


Scenario: List accounts
Given I have created a customer
When I GET to /accounts
Then I should get a 200 OK status code
And the response is valid according to the "accounts" schema

Scenario: Retrieving accounts for a customer
Given I have created a customer
When I GET to /customers/:customer_id/accounts
Then I should get a 200 OK status code
And the response is valid according to the "accounts" schema
And the fields on these credits match:
"""
{
"links": {
"customer": ":customer_id"
}
}
"""

Scenario: Credit a customer deposit account
Given I have an order with a debit
When I POST to /accounts/:customer_deposit_account_id/credits
"""
{
"credits": [{
"amount": 234,
"order": "/orders/:order_id"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it return the order href? i think we return the order ID and a link href.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The response should. Most of the steps in these API specs are just written to get the ID so I followed that convention for now, but I'd like to revisit it later.

}]
}
"""
Then I should get a 201 Created status code
And the response is valid according to the "credits" schema
47 changes: 47 additions & 0 deletions features/credits.feature
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,50 @@ Feature: Credits
"category_code": "no-funding-destination"
}
"""


Scenario: Bulk credit to a customer deposit account
Given I have a merchant with 2 orders with debits
When I POST to /accounts/:customer_deposit_account_id/credits with the body:
"""
{
"credits": [{
"amount": 1000,
"order": "/orders/:order_id_1",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would the credit say its destination is the sweep_account?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I reverse a credit before it's at the sweep_account, will it immediately go back into the order?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would the credit say its destination is the sweep_account?

that's implied by the endpoint that you're POSTing to in this scenario.

If I reverse a credit before it's at the sweep_account, will it immediately go back into the order?

it immediately goes to the sweep, i think you mean bank account. if you reverse a credit it should leave the sweep with a negative balance (if the credit has cleared to the bank account). in that case it would eventually debit the bank account to zero out the balance.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mjallday What do you mean by "eventually"? When would it actually debit the bank account? What would happen if the bank account doesn't have any balance?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the sum of the transactions to the sweep account since it was last settled are less than zero then it would result in a debit to the bank account.

we haven't defined how that would react in the case of the underlying bank account failing but i suspect it would cause the other transactions to go into a failed state.

"appears_on_statement_as": "Payout group A"
}]
}
"""
Then I should get a 201 Created status code
And the response is valid according to the "credits" schema

When I make a GET request to /accounts/:customer_deposit_account_id
Then I should get a 200 OK status code
And the fields on this error match:
"""
{
"balance": 1000
}
"""

When I POST to /accounts/:customer_deposit_account_id/credits with the body:
"""
{
"credits": [{
"amount": 1000,
"order": "/orders/:order_id_2",
"appears_on_statement_as": "Payout group B"
}]
}
"""
Then I should get a 201 Created status code
And the response is valid according to the "credits" schema

When I make a GET request to /accounts/:customer_deposit_account_id/credits
Then I should get a 200 OK status code
And the fields on this error match:
"""
{
"balance": 2000
}
"""
108 changes: 108 additions & 0 deletions features/settlements.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
@focus
Feature: Settlements
Settlement is the action of moving money out of an Account to a
bank account.

Scenario: Create a settlement
Given I have an Account with sufficient funds
And I have tokenized a bank account
When I POST to /accounts/:customer_deposit_account_id/settlements with the body:
"""
{
"settlements": [{
"destination": "/bank_accounts/:bank_account_id",
"appears_on_statement_as": "Settlement Oct",
"description": "Settlement for payouts from October"
}]
}
"""
Then I should get a 201 Created status code
And the response is valid according to the "settlements" schema
And the fields on this settlement match:
"""
{
"amount": "10000",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i may not understand the spec dependencies, how do you know that there was 100.00 in the account to settle beforehand?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the frustrating part of these steps. They're not yet very robust in terms of what you can pass through the many steps it takes to orchestrate an entire situation like this. Right now I know "I have an Account with sufficient funds" creates 3 orders each with a debit of 10000 and credits 10000 from each of the 3 into the customer sweep account.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

omg... ok, i understand how it works then. maybe we should change the fixture to "I have a sweep account with a balanced of $100". that way it's more explicit that what the balance is. Or maybe "I have 3 orders for $100 and I those orders for $100 to a sweep account".

Hmmm, I see your frustration.

"appears_on_statement_as": "BAL*Settlement Oct",
"description": "Settlement for payouts from October",
"links": {
"destination": ":bank_account_id"
}
}
"""

Scenario: List settlements
Given I have 2 settlements
When I GET to /settlements
Then I should get a 200 OK status code
And the response is valid according to the "settlements" schema

Scenario: Retrieving settlements for a bank account
Given I have a bank account with a settlement
When I GET to /bank_accounts/:bank_account_id/settlements
Then I should get a 200 OK status code
And the response is valid according to the "settlements" schema
And the fields on these settlements match:
"""
{
"links": {
"destination": ":bank_account_id"
}
}
"""

Scenario: Updating a settlement description
Given I have a bank account with a settlement
When I PUT to /settlements/:settlement_id with the body:
"""
{
"description": "A new settlement description"
}
"""
Then I should get a 200 OK status code
And the response is valid according to the "settlements" schema
And the fields on this settlement match:
"""
{
"description": "A new settlement description"
}
"""

Scenario: Updating a settlement meta
Given I have a bank account with a settlement
When I PUT to /settlements/:settlement_id with the body:
"""
{
"meta": {
"reference_number": "546512"
}
}
"""
Then I should get a 200 OK status code
And the response is valid according to the "settlements" schema
And the fields on this settlement match:
"""
{
"meta": {
"reference_number": "546512"
}
}
"""

Scenario: Creating a settlement without a funding destination leads to failure
When I POST to /settlements with the body:
"""
{
"settlements": [{
"description": "Will this credit work? Nobody knows"
}]
}
"""
Then I should get a 409 status code
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currently this is a 400

curl https://api.balancedpayments.com/debits      -H "Accept: application/vnd.api+json;revision=1.1"      -u ak-test-D4i8c5COSld5I3XkSkXIUBLtGhpN6X67:      -d "appears_on_statement_as=Statement text"      -d "amount=5000"      -d "description=Some descriptive text for the debit in the dashboard"
{
  "errors": [
    {
      "status": "Bad Request",
      "category_code": "request",
      "additional": null,
      "status_code": 400,
      "category_type": "request",
      "extras": {
        "source": "Missing required field [source]"
      },
      "request_id": "OHM61d85cea597611e4bc0006429171ffad",
      "description": "Missing required field [source] Your request id is OHM61d85cea597611e4bc0006429171ffad."
    }
  ]
}

And the response is valid according to the "errors" schema
And the fields on this error match:
"""
{
"category_code": "no-funding-destination"
}
"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about when there's no transactions to settle?


8 changes: 8 additions & 0 deletions features/step_definitions/account.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Given(/^I have an Account with sufficient funds$/) do
step 'I have an order with a debit'
@client.post("/accounts/#{@customer_deposit_account_id}/credits", {
amount: 10000,
order: @order_id
})
@client.add_hydrate :credit_id, @client['id']
end
8 changes: 7 additions & 1 deletion features/step_definitions/customers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@
)
@customer_id = @client['id']
@client.add_hydrate :customer_id, @customer_id

# get the deposit account
@client['accounts'].each do |acct|
if acct['type'] == 'deposit'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type attribute on account here?

@client.add_hydrate :customer_deposit_account_id, acct['id']
break
end
end
@customer_url = @client['customers']['href']

# tokenize a card for them
Expand Down
20 changes: 20 additions & 0 deletions features/step_definitions/orders.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,24 @@
order: @order_id
})
@client.add_hydrate :card_hold_id, @client['id']
end


Given(/^I have a merchant with (\d) orders with debits$/) do |num|
step 'I have created a customer'
@client.post('/customers', {})
@merchant_id = @client['id']
@client.add_hydrate :merchant_id, @merchant_id
step 'I have tokenized a bank account and associated with the merchant'
num.to_i.times do |i|
@client.post("/customers/#{@merchant_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, @merchant_id
instance_variable_set("@order_id_#{i + 1}", order_id)
end
end
14 changes: 14 additions & 0 deletions features/step_definitions/settlements.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Given(/^I have a bank account with a settlement$/) do
step 'I have an order with a debit'
step 'I have created a customer'
@client.post("/accounts/#{@customer_deposit_account_id}/credits", {
amount: 1234,
order: :order_id
})
@settlement_id = @client['settlements']['id']
@client.add_hydrate :settlement_id, @settlement_id
end

Given(/^I have (\d) settlements$/) do
num.to_i.times { step 'I have a bank account with a settlement' }
end
69 changes: 69 additions & 0 deletions fixtures/_models/account.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "The redeemable balance of a customer is represented as a financial instrument just like a Card or Bank Account.",
"type": "object",
"properties": {
"id": {
"type": "string",
"pattern": "AT[a-zA-Z0-9]{16,32}"
},
"href": {
"type": "string",
"format": "uri"
},
"created_at": {
"type": "string",
"format": "date-time"
},
"updated_at": {
"type": "string",
"format": "date-time"
},
"balance": {
"type": "integer",
"minimum": 0
},
"currency": {
"type": "string",
"enum": [
"USD"
]
},
"can_debit": {
"description": "Flag indicating whether this account can be debited (true) or not (false).",
"type": "boolean"
},
"can_credit": {
"description": "Flag indicating whether this account instrument can be credited (true) or not (false).",
"type": "boolean"
},
"meta": {
"type": "object"
},
"links": {
"type": "object",
"properties": {
"customer": {
"type": "string",
"pattern": "CU[a-zA-Z0-9]{16,32}"
}
},
"additionalProperties": false,
"required": [
"customer"
]
}
},
"required": [
"id",
"href",
"created_at",
"updated_at",
"balance",
"currency",
"can_credit",
"can_debit",
"meta",
"links"
]
}
8 changes: 8 additions & 0 deletions fixtures/_models/bank_account.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@
"string"
],
"pattern": "BZ[a-zA-Z0-9]{16,32}"
},
"sweep_account": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this here/necessary?

"description": "A funding instrument representing a sweep account used for bulk credits",
"type": [
"null",
"string"
],
"pattern": "(AT)[a-zA-Z0-9]{16,32}"
}
},
"required": [
Expand Down
2 changes: 1 addition & 1 deletion fixtures/_models/credit.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
"destination": {
"description": "The funding destination for this credit",
"type": "string",
"pattern": "(CC|BA)[a-zA-Z0-9]{16,32}"
"pattern": "(CC|BA|AT)[a-zA-Z0-9]{16,32}"
},
"order": {
"description": "The order this credit is associated with",
Expand Down
2 changes: 1 addition & 1 deletion fixtures/_models/reversal.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
"pattern": "CR[a-zA-Z0-9]{16,32}"
},
"order": {
"description": "The order this credit is assocaited with",
"description": "The order this credit is associated with",
"type": [
"null",
"string"
Expand Down
Loading