solidus_signifyd 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +21 -0
- data/Gemfile +10 -3
- data/README.md +58 -1
- data/app/controllers/spree/api/spree_signifyd/orders_controller.rb +1 -1
- data/app/models/spree/signifyd_configuration.rb +1 -0
- data/app/models/spree_signifyd/order_concerns.rb +18 -1
- data/app/models/spree_signifyd/shipment_decorator.rb +1 -1
- data/app/serializers/spree_signifyd/credit_card_serializer.rb +9 -9
- data/app/serializers/spree_signifyd/order_serializer.rb +22 -10
- data/app/serializers/spree_signifyd/user_serializer.rb +13 -2
- data/lib/spree_signifyd.rb +2 -3
- data/lib/spree_signifyd/create_signifyd_case.rb +3 -4
- data/lib/spree_signifyd/engine.rb +1 -0
- data/solidus_signifyd.gemspec +4 -4
- data/spec/controllers/spree/api/spree_signifyd/orders_controller_spec.rb +6 -10
- data/spec/lib/spree_signifyd/create_signifyd_case_spec.rb +4 -4
- data/spec/lib/spree_signifyd_spec.rb +14 -3
- data/spec/models/spree/order_spec.rb +38 -11
- data/spec/models/spree/shipment_spec.rb +33 -35
- data/spec/serializers/spree_signifyd/billing_address_serializer.rb +1 -1
- data/spec/serializers/spree_signifyd/credit_card_serializer_spec.rb +2 -2
- data/spec/serializers/spree_signifyd/delivery_address_serializer_spec.rb +2 -2
- data/spec/serializers/spree_signifyd/order_serializer_spec.rb +58 -17
- data/spec/serializers/spree_signifyd/user_serializer_spec.rb +9 -1
- data/spec/spec_helper.rb +7 -0
- data/spec/support/api_schema_matcher.rb +9 -0
- data/spec/support/schemas/v2/case.json +305 -0
- metadata +46 -21
@@ -13,7 +13,7 @@ describe Spree::Order, :type => :model do
|
|
13
13
|
subject { order.is_risky? }
|
14
14
|
|
15
15
|
context "no signifyd_score" do
|
16
|
-
it {
|
16
|
+
it { is_expected.to eq false }
|
17
17
|
end
|
18
18
|
|
19
19
|
context "signifyd_score present" do
|
@@ -21,31 +21,58 @@ describe Spree::Order, :type => :model do
|
|
21
21
|
|
22
22
|
context "approved" do
|
23
23
|
before { SpreeSignifyd.approve(order: order) }
|
24
|
-
it {
|
24
|
+
it { is_expected.to eq false }
|
25
25
|
end
|
26
26
|
|
27
27
|
context "not approved" do
|
28
|
-
it {
|
28
|
+
it { is_expected.to eq true }
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
33
|
describe "transition to complete" do
|
34
34
|
let(:order) { create(:order_with_line_items, state: 'confirm') }
|
35
|
-
let!(:payment) { create(:payment, amount: order.total, order: order ) }
|
36
35
|
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
shared_examples "an order we send to signifyd" do
|
37
|
+
it "creates a new SIGNIFYD case" do
|
38
|
+
expect(SpreeSignifyd).to receive(:create_case).with(order_number: order.number)
|
39
|
+
order.complete!
|
40
|
+
end
|
40
41
|
end
|
41
42
|
|
42
|
-
|
43
|
-
it "does not create a case" do
|
44
|
-
order.contents.approve(user: Spree.user_class.first)
|
43
|
+
shared_examples "an order we DO NOT send to signifyd" do
|
44
|
+
it "does not create a new SIGNIFYD case" do
|
45
45
|
expect(SpreeSignifyd).not_to receive(:create_case)
|
46
46
|
order.complete!
|
47
47
|
end
|
48
48
|
end
|
49
|
-
end
|
50
49
|
|
50
|
+
context "paid with store credit only" do
|
51
|
+
let!(:payment) { create(:store_credit_payment, amount: order.total, order: order ) }
|
52
|
+
|
53
|
+
it_behaves_like "an order we send to signifyd"
|
54
|
+
|
55
|
+
context "don't send store credit orders to SIGNIFYD" do
|
56
|
+
before { SpreeSignifyd::Config[:exclude_store_credit_orders] = true }
|
57
|
+
|
58
|
+
it_behaves_like "an order we DO NOT send to signifyd"
|
59
|
+
|
60
|
+
it "is immediately approved" do
|
61
|
+
expect{ order.complete! }.to change{ order.approved? }.from(false).to(true)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "paid with cash" do
|
67
|
+
let!(:payment) { create(:payment, amount: order.total, order: order ) }
|
68
|
+
|
69
|
+
it_behaves_like "an order we send to signifyd"
|
70
|
+
|
71
|
+
context "the order is already approved" do # e.g. unreturned exchanges are automatically approved
|
72
|
+
before { order.contents.approve(user: Spree.user_class.first) }
|
73
|
+
|
74
|
+
it_behaves_like "an order we DO NOT send to signifyd"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
51
78
|
end
|
@@ -5,52 +5,50 @@ describe Spree::Shipment, :type => :model do
|
|
5
5
|
let(:shipment) { create(:shipment) }
|
6
6
|
subject { shipment.determine_state(shipment.order) }
|
7
7
|
|
8
|
-
describe "#
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
context "the order is not approved" do
|
14
|
-
it "returns pending" do
|
15
|
-
shipment.order.stub(:approved?).and_return(false)
|
16
|
-
subject.should eq "pending"
|
17
|
-
end
|
8
|
+
describe "#determine_state" do
|
9
|
+
context "with a canceled order" do
|
10
|
+
before do
|
11
|
+
shipment.order.update(state: 'canceled')
|
12
|
+
shipment.update(state: 'canceled')
|
18
13
|
end
|
19
14
|
|
20
|
-
|
21
|
-
|
22
|
-
shipment.order.stub(:approved?).and_return(true)
|
23
|
-
shipment.should_receive(:determine_state).with(shipment.order)
|
24
|
-
subject
|
25
|
-
end
|
15
|
+
it "canceled shipments remain canceled" do
|
16
|
+
expect(subject).to eq "canceled"
|
26
17
|
end
|
27
18
|
end
|
28
19
|
|
29
|
-
context "
|
30
|
-
before { shipment.order.
|
20
|
+
context "with an approved order" do
|
21
|
+
before { shipment.order.contents.approve(name: 'test approver') }
|
31
22
|
|
32
|
-
it "
|
33
|
-
|
34
|
-
subject
|
23
|
+
it "pending shipments remain pending" do
|
24
|
+
expect(subject).to eq "pending"
|
35
25
|
end
|
36
|
-
end
|
37
26
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
it
|
43
|
-
|
44
|
-
subject
|
27
|
+
describe "regular Solidus behaviour" do
|
28
|
+
context "order cannot ship" do
|
29
|
+
before { allow(shipment.order).to receive_messages can_ship?: false }
|
30
|
+
|
31
|
+
it 'returns pending' do
|
32
|
+
expect(subject).to eq 'pending'
|
45
33
|
end
|
46
34
|
end
|
47
|
-
end
|
48
35
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
it
|
53
|
-
|
36
|
+
context "order can ship" do
|
37
|
+
before { allow(shipment.order).to receive_messages can_ship?: true }
|
38
|
+
|
39
|
+
it 'returns shipped when already shipped' do
|
40
|
+
allow(shipment).to receive_messages state: 'shipped'
|
41
|
+
expect(subject).to eq 'shipped'
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'returns pending when unpaid' do
|
45
|
+
allow(shipment.order).to receive_messages paid?: false
|
46
|
+
expect(subject).to eq 'pending'
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'returns ready when paid' do
|
50
|
+
allow(shipment.order).to receive_messages paid?: true
|
51
|
+
expect(subject).to eq 'ready'
|
54
52
|
end
|
55
53
|
end
|
56
54
|
end
|
@@ -6,7 +6,7 @@ module SpreeSignifyd
|
|
6
6
|
let(:serialized_address) { JSON.parse(BillingAddressSerializer.new(bill_address).to_json) }
|
7
7
|
|
8
8
|
context "node values" do
|
9
|
-
it { serialized_address.
|
9
|
+
it { expect(serialized_address).to include 'billingAddress' }
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
@@ -15,11 +15,11 @@ module SpreeSignifyd
|
|
15
15
|
end
|
16
16
|
|
17
17
|
it "expiryMonth" do
|
18
|
-
expect(serialized_credit_card['expiryMonth']).to eq credit_card.month
|
18
|
+
expect(serialized_credit_card['expiryMonth']).to eq credit_card.month.to_i
|
19
19
|
end
|
20
20
|
|
21
21
|
it "expiryYear" do
|
22
|
-
expect(serialized_credit_card['expiryYear']).to eq credit_card.year
|
22
|
+
expect(serialized_credit_card['expiryYear']).to eq credit_card.year.to_i
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
@@ -6,8 +6,8 @@ module SpreeSignifyd
|
|
6
6
|
let(:serialized_address) { JSON.parse(DeliveryAddressSerializer.new(delivery_address).to_json) }
|
7
7
|
|
8
8
|
context "node values" do
|
9
|
-
it { serialized_address.
|
10
|
-
it { serialized_address['fullName'].
|
9
|
+
it { expect(serialized_address).to include 'deliveryAddress' }
|
10
|
+
it { expect(serialized_address['fullName']).to eq delivery_address.full_name }
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
@@ -2,54 +2,95 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module SpreeSignifyd
|
4
4
|
describe OrderSerializer do
|
5
|
-
let(:order) { create
|
5
|
+
let(:order) { create :shipped_order,
|
6
|
+
line_items_count: 1,
|
7
|
+
last_ip_address: "127.0.0.1"
|
8
|
+
}
|
6
9
|
let(:line_item) { order.line_items.first }
|
7
10
|
let(:serialized_order) { JSON.parse(OrderSerializer.new(order).to_json) }
|
8
11
|
|
12
|
+
describe 'document format' do
|
13
|
+
before do
|
14
|
+
# we can't pass payments into the :shipped_order factory
|
15
|
+
order.payments.last.update(avs_response: "M", cvv_response_code: "M")
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'matches the SIGNIFYD V2 api' do
|
19
|
+
expect(serialized_order).to match_schema('v2/case.json')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
9
23
|
describe "node values" do
|
10
24
|
context "purchase" do
|
11
25
|
|
12
26
|
let(:purchase) { serialized_order['purchase'] }
|
13
27
|
|
14
|
-
it { purchase['browserIpAddress'].
|
15
|
-
it { purchase['orderId'].
|
16
|
-
it { purchase['createdAt'].
|
17
|
-
it { purchase['currency'].
|
18
|
-
it { purchase['totalPrice'].
|
28
|
+
it { expect(purchase['browserIpAddress']).to eq order.last_ip_address }
|
29
|
+
it { expect(purchase['orderId']).to eq order.number }
|
30
|
+
it { expect(purchase['createdAt']).to eq order.completed_at.utc.iso8601 }
|
31
|
+
it { expect(purchase['currency']).to eq order.currency }
|
32
|
+
it { expect(purchase['totalPrice']).to eq order.total }
|
19
33
|
|
20
34
|
context "with a payment" do
|
21
|
-
it { purchase['avsResponseCode'].
|
22
|
-
it { purchase['cvvResponseCode'].
|
35
|
+
it { expect(purchase['avsResponseCode']).to eq order.payments.last.avs_response.to_s }
|
36
|
+
it { expect(purchase['cvvResponseCode']).to eq order.payments.last.cvv_response_code.to_s }
|
37
|
+
|
38
|
+
context "when the payment is a paypal payment" do
|
39
|
+
before do
|
40
|
+
order.payments.first.source.update({
|
41
|
+
cc_type: "paypal"
|
42
|
+
})
|
43
|
+
end
|
44
|
+
|
45
|
+
it "includes a paymentGateway specification for signifyd" do
|
46
|
+
expect(purchase['paymentGateway']).to eql("paypal_account")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "when the payment is not a paypal payment" do
|
51
|
+
it "does not include a paymentGateway key" do
|
52
|
+
expect(purchase['paymentGateway']).to eql(nil)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "paid with store credit" do
|
58
|
+
before do
|
59
|
+
create(:store_credit_payment, amount: order.total, order: order)
|
60
|
+
order.payments.reload
|
61
|
+
end
|
62
|
+
|
63
|
+
it { expect(serialized_order["card"]).to eq({}) }
|
23
64
|
end
|
24
65
|
|
25
66
|
context "without a payment" do
|
26
67
|
let(:order) { create(:completed_order_with_totals) }
|
27
68
|
|
28
|
-
it { purchase['avsResponseCode'].
|
29
|
-
it { purchase['cvvResponseCode'].
|
69
|
+
it { expect(purchase['avsResponseCode']).to eq "" }
|
70
|
+
it { expect(purchase['cvvResponseCode']).to eq "" }
|
30
71
|
end
|
31
72
|
|
32
73
|
it "contains a products node" do
|
33
|
-
purchase['products'].
|
74
|
+
expect(purchase['products']).to eq [ JSON.parse(SpreeSignifyd::LineItemSerializer.new(line_item).to_json) ]
|
34
75
|
end
|
35
76
|
end
|
36
77
|
|
37
78
|
context "userAccount" do
|
38
|
-
it { serialized_order.
|
79
|
+
it { expect(serialized_order).to include 'userAccount' }
|
39
80
|
end
|
40
81
|
|
41
82
|
context "recipient" do
|
42
|
-
it { serialized_order.
|
43
|
-
it { serialized_order["recipient"]["confirmationEmail"].
|
83
|
+
it { expect(serialized_order).to include 'recipient' }
|
84
|
+
it { expect(serialized_order["recipient"]["confirmationEmail"]).to eq order.email }
|
44
85
|
end
|
45
86
|
|
46
87
|
context "card" do
|
47
|
-
it { serialized_order.
|
88
|
+
it { expect(serialized_order).to include 'card' }
|
48
89
|
|
49
90
|
context "credit card payment" do
|
50
91
|
let!(:payment) { create(:payment, order: order) }
|
51
92
|
|
52
|
-
it { serialized_order["card"].
|
93
|
+
it { expect(serialized_order["card"]).to include 'billingAddress'}
|
53
94
|
end
|
54
95
|
|
55
96
|
context "no payment source" do
|
@@ -62,7 +103,7 @@ module SpreeSignifyd
|
|
62
103
|
|
63
104
|
context "non credit card payment" do
|
64
105
|
it "contains no data" do
|
65
|
-
Spree::CreditCard.
|
106
|
+
allow_any_instance_of(Spree::CreditCard).to receive(:instance_of?).and_return(false)
|
66
107
|
expect(serialized_order["card"]).to eq({})
|
67
108
|
end
|
68
109
|
end
|
@@ -9,6 +9,10 @@ module SpreeSignifyd
|
|
9
9
|
|
10
10
|
let(:serialized_user) { JSON.parse(UserSerializer.new(user).to_json) }
|
11
11
|
|
12
|
+
before do
|
13
|
+
old_complete_order.update_attributes(completed_at: 30.days.ago)
|
14
|
+
end
|
15
|
+
|
12
16
|
context "node values" do
|
13
17
|
it "emailAddress" do
|
14
18
|
expect(serialized_user['emailAddress']).to eq user.email
|
@@ -18,6 +22,10 @@ module SpreeSignifyd
|
|
18
22
|
expect(serialized_user['username']).to eq user.email
|
19
23
|
end
|
20
24
|
|
25
|
+
it "phone" do
|
26
|
+
expect(serialized_user['phone']).to eq new_complete_order.ship_address.phone
|
27
|
+
end
|
28
|
+
|
21
29
|
it "createdDate" do
|
22
30
|
expect(serialized_user['createdDate']).to eq user.created_at.utc.iso8601
|
23
31
|
end
|
@@ -46,7 +54,7 @@ module SpreeSignifyd
|
|
46
54
|
end
|
47
55
|
|
48
56
|
it "aggregateOrderDollars" do
|
49
|
-
expect(serialized_user['aggregateOrderDollars']).to eq (old_complete_order.total + new_complete_order.total)
|
57
|
+
expect(serialized_user['aggregateOrderDollars']).to eq (old_complete_order.total + new_complete_order.total)
|
50
58
|
end
|
51
59
|
end
|
52
60
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -38,6 +38,7 @@ RSpec.configure do |config|
|
|
38
38
|
config.run_all_when_everything_filtered = true
|
39
39
|
config.use_transactional_fixtures = false
|
40
40
|
|
41
|
+
config.include RSpec::Rails::Matchers
|
41
42
|
config.include FactoryGirl::Syntax::Methods
|
42
43
|
|
43
44
|
# Ensure Suite is set to use transactions for speed.
|
@@ -46,10 +47,16 @@ RSpec.configure do |config|
|
|
46
47
|
DatabaseCleaner.clean_with :truncation
|
47
48
|
end
|
48
49
|
|
50
|
+
# allow us to test various preference settings without cross contamination
|
51
|
+
config.before :each do
|
52
|
+
SpreeSignifyd::Config.reset
|
53
|
+
end
|
54
|
+
|
49
55
|
# Before each spec check if it is a Javascript test and switch between using database transactions or not where necessary.
|
50
56
|
config.before :each do
|
51
57
|
DatabaseCleaner.strategy = example.metadata[:js] ? :truncation : :transaction
|
52
58
|
DatabaseCleaner.start
|
59
|
+
ActiveJob::Base.queue_adapter = :test
|
53
60
|
|
54
61
|
allow(Signifyd::Case).to receive(:create).and_return(
|
55
62
|
{ code: 201, body: { investigationId: 123 } }
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require "json-schema"
|
2
|
+
|
3
|
+
RSpec::Matchers.define :match_schema do |schema|
|
4
|
+
match do |candidate|
|
5
|
+
schema_directory = File.expand_path("../schemas", __FILE__)
|
6
|
+
schema_path = File.join(schema_directory, schema)
|
7
|
+
JSON::Validator.validate!(schema_path, candidate)
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,305 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
3
|
+
"type": "object",
|
4
|
+
"properties": {
|
5
|
+
"purchase": {
|
6
|
+
"type": "object",
|
7
|
+
"description": "The purchase.",
|
8
|
+
"properties": {
|
9
|
+
"browserIpAddress": {
|
10
|
+
"type": "string",
|
11
|
+
"description": "The IP Address of the browser that was used to make the This is the IP Address that was used to connect to your site and make the "
|
12
|
+
},
|
13
|
+
"shipments": {
|
14
|
+
"type": "array",
|
15
|
+
"description": "The shipments association with this purchase"
|
16
|
+
},
|
17
|
+
"products": {
|
18
|
+
"type": "array",
|
19
|
+
"description": "The products purchased in the transaction."
|
20
|
+
},
|
21
|
+
"orderId": {
|
22
|
+
"type": "string",
|
23
|
+
"description": "A string uniquely identifying this order."
|
24
|
+
},
|
25
|
+
"createdAt": {
|
26
|
+
"type": "string",
|
27
|
+
"description": "`yyyy-MM-dd'T'HH:mm:ssZ` The date and time when the order was placed, shown on the signifyd console. See the Dates section of these docs for more information about date formats."
|
28
|
+
},
|
29
|
+
"paymentGateway": {
|
30
|
+
"type": "string",
|
31
|
+
"description": "The gateway that processed the transaction. For paypal orders use paypal_account."
|
32
|
+
},
|
33
|
+
"paymentMethod": {
|
34
|
+
"type": "object",
|
35
|
+
"description": "The method the user used to complete the "
|
36
|
+
},
|
37
|
+
"transactionId": {
|
38
|
+
"type": "string",
|
39
|
+
"description": "The unique identifier provided by the payment gateway for this order. If you have provided us with credentials for your payment gateway we can obtain additional details about the order, like AVS and CVV status, from your payment provider."
|
40
|
+
},
|
41
|
+
"currency": {
|
42
|
+
"type": "string",
|
43
|
+
"description": "The currency type of the order, in 3 letter ISO 4217 format. Defaults to USD."
|
44
|
+
},
|
45
|
+
"avsResponseCode": {
|
46
|
+
"type": "string",
|
47
|
+
"description": "The response code from the address verification system (AVS). Accepted codes: http://www.emsecommerce.net/avs_cvv2_response_codes.htm"
|
48
|
+
},
|
49
|
+
"cvvResponseCode": {
|
50
|
+
"type": "string",
|
51
|
+
"description": "The response code from the card verification value (CVV) check. Accepted codes listed on above link."
|
52
|
+
},
|
53
|
+
"orderChannel": {
|
54
|
+
"type": "string",
|
55
|
+
"description": "The method used by the buyer to place the order. Either WEB or PHONE."
|
56
|
+
},
|
57
|
+
"receivedBy": {
|
58
|
+
"type": "string",
|
59
|
+
"description": "If the order was was taken by a customer service or sales agent, his or her name."
|
60
|
+
},
|
61
|
+
"totalPrice": {
|
62
|
+
"type": "number",
|
63
|
+
"description": "The total price of the order, including shipping price and taxes."
|
64
|
+
}
|
65
|
+
},
|
66
|
+
"required": [
|
67
|
+
"browserIpAddress",
|
68
|
+
"orderId",
|
69
|
+
"createdAt",
|
70
|
+
"avsResponseCode",
|
71
|
+
"cvvResponseCode",
|
72
|
+
"totalPrice"
|
73
|
+
]
|
74
|
+
},
|
75
|
+
"recipient": {
|
76
|
+
"type": "object",
|
77
|
+
"description": "The person receiving the goods.",
|
78
|
+
"properties": {
|
79
|
+
"fullName": {
|
80
|
+
"type": "string",
|
81
|
+
"description": "The full name of the person receiving the goods. If this item is being shipped, then this field is the person it is being shipping to. Don't assume this name is the same as card.cardHolderName. Only put a value here if the name will actually appear on the shipping label. If this item is digital, then this field will likely be blank."
|
82
|
+
},
|
83
|
+
"confirmationEmail": {
|
84
|
+
"type": "string",
|
85
|
+
"description": "When this purchase was completed, you likely sent a confirmation email or you will be sending a confirmation email to someone once you approve the order. This is the email address to which that confirmation email will be sent."
|
86
|
+
},
|
87
|
+
"confirmationPhone": {
|
88
|
+
"type": "string",
|
89
|
+
"description": "The phone number that you would call if there was something wrong with this order or the phone number that was supplied with the shipping information."
|
90
|
+
},
|
91
|
+
"organization": {
|
92
|
+
"type": "string",
|
93
|
+
"description": "If provided by the buyer, the name of the recipient's company or organization."
|
94
|
+
},
|
95
|
+
"deliveryAddress": {
|
96
|
+
"type": "object",
|
97
|
+
"description": "The address to which the order will be delivered.",
|
98
|
+
"properties": {
|
99
|
+
"streetAddress": {
|
100
|
+
"type": "string",
|
101
|
+
"description": "The street number and street name."
|
102
|
+
},
|
103
|
+
"unit": {
|
104
|
+
"type": "string",
|
105
|
+
"description": "The unit or apartment number."
|
106
|
+
},
|
107
|
+
"city": {
|
108
|
+
"type": "string",
|
109
|
+
"description": "The city name."
|
110
|
+
},
|
111
|
+
"provinceCode": {
|
112
|
+
"type": "string",
|
113
|
+
"description": "The code or abbreviation for the province."
|
114
|
+
},
|
115
|
+
"postalCode string": {
|
116
|
+
"type": "string",
|
117
|
+
"description": "The postal code."
|
118
|
+
},
|
119
|
+
"countryCode": {
|
120
|
+
"type": "string",
|
121
|
+
"description": "The two-letter ISO-3166 country code. If left blank, we will assume US."
|
122
|
+
},
|
123
|
+
"latitude": {
|
124
|
+
"type": "number",
|
125
|
+
"description": "The latitude of the address. Used when address details are not provided. Ignored otherwise."
|
126
|
+
},
|
127
|
+
"longitude": {
|
128
|
+
"type": "number",
|
129
|
+
"description": "The longitude of the address. Used when address details are not provided. Ignored otherwise."
|
130
|
+
}
|
131
|
+
}
|
132
|
+
}
|
133
|
+
},
|
134
|
+
"required": [
|
135
|
+
"fullName",
|
136
|
+
"confirmationEmail",
|
137
|
+
"deliveryAddress"
|
138
|
+
]
|
139
|
+
},
|
140
|
+
"card": {
|
141
|
+
"type": "object",
|
142
|
+
"properties": {
|
143
|
+
"cardHolderName": {
|
144
|
+
"type": "string",
|
145
|
+
"description": "The full name on the credit card that was charged."
|
146
|
+
},
|
147
|
+
"bin": {
|
148
|
+
"type": "number",
|
149
|
+
"description": "The first six digits of the credit card, the bank identification number, which uniquely identifies the issuer."
|
150
|
+
},
|
151
|
+
"last4": {
|
152
|
+
"type": "string",
|
153
|
+
"description": "The last four digits of the card number."
|
154
|
+
},
|
155
|
+
"expiryMonth": {
|
156
|
+
"type": "number",
|
157
|
+
"description": "MM representation of the expiration month of the card."
|
158
|
+
},
|
159
|
+
"expiryYear": {
|
160
|
+
"type": "number",
|
161
|
+
"description": "yyyy representation of the expiration year of the card."
|
162
|
+
},
|
163
|
+
"billingAddress": {
|
164
|
+
"type": "object",
|
165
|
+
"properties": {
|
166
|
+
"streetAddress": {
|
167
|
+
"type": "string",
|
168
|
+
"description": "The street number and street name."
|
169
|
+
},
|
170
|
+
"unit": {
|
171
|
+
"type": "string",
|
172
|
+
"description": "The unit or apartment number."
|
173
|
+
},
|
174
|
+
"city": {
|
175
|
+
"type": "string",
|
176
|
+
"description": "The city name."
|
177
|
+
},
|
178
|
+
"provinceCode": {
|
179
|
+
"type": "string",
|
180
|
+
"description": "The code or abbreviation for the province."
|
181
|
+
},
|
182
|
+
"postalCode string": {
|
183
|
+
"type": "string",
|
184
|
+
"description": "The postal code."
|
185
|
+
},
|
186
|
+
"countryCode": {
|
187
|
+
"type": "string",
|
188
|
+
"description": "The two-letter ISO-3166 country code. If left blank, we will assume US."
|
189
|
+
},
|
190
|
+
"latitude": {
|
191
|
+
"type": "number",
|
192
|
+
"description": "The latitude of the address. Used when address details are not provided. Ignored otherwise."
|
193
|
+
},
|
194
|
+
"longitude": {
|
195
|
+
"type": "number",
|
196
|
+
"description": "The longitude of the address. Used when address details are not provided. Ignored otherwise."
|
197
|
+
}
|
198
|
+
},
|
199
|
+
"description": "The billing address for the card."
|
200
|
+
}
|
201
|
+
},
|
202
|
+
"required": [
|
203
|
+
"cardHolderName",
|
204
|
+
"billingAddress"
|
205
|
+
]
|
206
|
+
},
|
207
|
+
"userAccount": {
|
208
|
+
"type": "object",
|
209
|
+
"properties": {
|
210
|
+
"email": {
|
211
|
+
"type": "string",
|
212
|
+
"description": "The primary email address associated with the account."
|
213
|
+
},
|
214
|
+
"username": {
|
215
|
+
"type": "string",
|
216
|
+
"description": "The username associated with the account. Please supply this even if it is the same as the email address."
|
217
|
+
},
|
218
|
+
"phone": {
|
219
|
+
"type": "string",
|
220
|
+
"description": "The phone number association with the account."
|
221
|
+
},
|
222
|
+
"createdDate": {
|
223
|
+
"type": "string",
|
224
|
+
"description": "`yyyy-MM-dd'T'HH:mm:ssZ` The date when the account was created. See the Dates section of these docs for more information about date formats."
|
225
|
+
},
|
226
|
+
"accountNumber": {
|
227
|
+
"type": "string",
|
228
|
+
"description": "Your unique identifier for the account."
|
229
|
+
},
|
230
|
+
"lastOrderId": {
|
231
|
+
"type": "string",
|
232
|
+
"description": "The unique identifier for the last order placed by this account, prior to the current order."
|
233
|
+
},
|
234
|
+
"aggregateOrderCount": {
|
235
|
+
"type": "number",
|
236
|
+
"description": "The total count of orders placed by this account since it was created, including the current order."
|
237
|
+
},
|
238
|
+
"aggregateOrderDollars": {
|
239
|
+
"type": "number",
|
240
|
+
"description": "The total amount spent by this account since it was created, including the current order."
|
241
|
+
},
|
242
|
+
"lastUpdateDate": {
|
243
|
+
"type": "string",
|
244
|
+
"description": "`yyyy-MM-dd'T'HH:mm:ssZ` The last time a change was made to this account other than an order being placed. Examples include changing email addresses or adding a new credit card. See the Dates section of these docs for more information about date formats."
|
245
|
+
}
|
246
|
+
}
|
247
|
+
},
|
248
|
+
"seller": {
|
249
|
+
"type": "object",
|
250
|
+
"properties": {
|
251
|
+
"name": {
|
252
|
+
"type": "string",
|
253
|
+
"description": "The business name of the seller."
|
254
|
+
},
|
255
|
+
"domain": {
|
256
|
+
"type": "string",
|
257
|
+
"description": "The domain of the seller."
|
258
|
+
},
|
259
|
+
"shipFromAddress": {
|
260
|
+
"type": "object",
|
261
|
+
"properties": {
|
262
|
+
"streetAddress": {
|
263
|
+
"type": "string",
|
264
|
+
"description": "The street number and street name."
|
265
|
+
},
|
266
|
+
"unit": {
|
267
|
+
"type": "string",
|
268
|
+
"description": "The unit or apartment number."
|
269
|
+
},
|
270
|
+
"city": {
|
271
|
+
"type": "string",
|
272
|
+
"description": "The city name."
|
273
|
+
},
|
274
|
+
"provinceCode": {
|
275
|
+
"type": "string",
|
276
|
+
"description": "The code or abbreviation for the province."
|
277
|
+
},
|
278
|
+
"postalCode string": {
|
279
|
+
"type": "string",
|
280
|
+
"description": "The postal code."
|
281
|
+
},
|
282
|
+
"countryCode": {
|
283
|
+
"type": "string",
|
284
|
+
"description": "The two-letter ISO-3166 country code. If left blank, we will assume US."
|
285
|
+
},
|
286
|
+
"latitude": {
|
287
|
+
"type": "number",
|
288
|
+
"description": "The latitude of the address. Used when address details are not provided. Ignored otherwise."
|
289
|
+
},
|
290
|
+
"longitude": {
|
291
|
+
"type": "number",
|
292
|
+
"description": "The longitude of the address. Used when address details are not provided. Ignored otherwise."
|
293
|
+
}
|
294
|
+
},
|
295
|
+
"description": "The location from which the seller shipped the order."
|
296
|
+
}
|
297
|
+
}
|
298
|
+
}
|
299
|
+
},
|
300
|
+
"required": [
|
301
|
+
"purchase",
|
302
|
+
"recipient",
|
303
|
+
"card"
|
304
|
+
]
|
305
|
+
}
|