Skip to content

Commit

Permalink
Introduce idempotency_key as post request option (#4)
Browse files Browse the repository at this point in the history
Introduce idempotency_key support
  • Loading branch information
rave-shift4 authored Jul 31, 2024
1 parent 3aaaa96 commit 2947d04
Show file tree
Hide file tree
Showing 16 changed files with 430 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
- uses: actions/checkout@v2
- uses: ruby/setup-ruby@v1
with:
ruby-version: 3.0
ruby-version: 2.6
bundler-cache: true
- name: Run style checks
run: bundle exec rubocop
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
shift4 (2.0.0)
shift4 (2.1.0)
httparty (~> 0.20)

GEM
Expand Down
1 change: 1 addition & 0 deletions lib/shift4.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
require 'shift4/fraud_warnings'
require 'shift4/payment_methods'
require 'shift4/plans'
require 'shift4/request_options'
require 'shift4/subscriptions'
require 'shift4/tokens'

Expand Down
3 changes: 3 additions & 0 deletions lib/shift4/communicator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ def self.request(json: nil, query: nil, body: nil, config: Configuration)
"Accept" => "application/json",
}
headers["Shift4-Merchant"] = config.merchant unless config.merchant.nil?
if config.is_a?(RequestOptions) && !config.idempotency_key.nil?
headers["Idempotency-Key"] = config.idempotency_key
end

if json
raise ArgumentError("Cannot specify both body and json") if body
Expand Down
24 changes: 24 additions & 0 deletions lib/shift4/request_options.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

module Shift4
class RequestOptions < Configuration
class << self
attr_reader :idempotency_key
end

def initialize(
config = Configuration,
idempotency_key: nil
)
super(
secret_key: config.secret_key,
merchant: config.merchant,
api_url: config.api_url,
uploads_url: config.uploads_url
)
@idempotency_key = idempotency_key
end

attr_reader :idempotency_key
end
end
2 changes: 1 addition & 1 deletion lib/shift4/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Shift4
VERSION = '2.0.0'
VERSION = '2.1.0'
end
23 changes: 23 additions & 0 deletions spec/integration/blacklist_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,29 @@
expect(retrieved['email']).to eq(email)
end

it 'create only one rule on blacklist with idempotency_key' do
# given
email = random_email
request = { ruleType: 'email', email: email, }

request_options = Shift4::RequestOptions.new(idempotency_key: random_idempotency_key.to_s)

created = Shift4::Blacklist.create(
request,
request_options
)

# when
not_created_because_idempotency = Shift4::Blacklist.create(
request,
request_options
)

# then
expect(created['id']).to eq(not_created_because_idempotency['id'])
expect(not_created_because_idempotency.headers['Idempotent-Replayed']).to eq("true")
end

it 'delete blacklist' do
# given
email = random_email
Expand Down
74 changes: 74 additions & 0 deletions spec/integration/cards_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,39 @@
expect(retrieved['customerId']).to eq(customer_id)
end

it 'create only one card with idempotency_key' do
# given
customer = Shift4::Customers.create(TestData.customer)
customer_id = customer['id']
cardholder_name = random_string
request_options = Shift4::RequestOptions.new(idempotency_key: random_idempotency_key.to_s)

created = Shift4::Cards.create(customer_id,
{
number: '4242424242424242',
expMonth: '12',
expYear: '2055',
cvc: '123',
cardholderName: cardholder_name
},
request_options)

# when
not_created_because_idempotency = Shift4::Cards.create(customer_id,
{
number: '4242424242424242',
expMonth: '12',
expYear: '2055',
cvc: '123',
cardholderName: cardholder_name
},
request_options)

# then
expect(created['id']).to eq(not_created_because_idempotency['id'])
expect(not_created_because_idempotency.headers['Idempotent-Replayed']).to eq("true")
end

it 'update card' do
# given
customer = Shift4::Customers.create(TestData.customer)
Expand Down Expand Up @@ -56,6 +89,47 @@
expect(updated_card['addressLine2']).to eq('updated addressLine2')
end

it 'update card only once with idempotency_key' do
# given
customer = Shift4::Customers.create(TestData.customer)
card = Shift4::Cards.create(customer['id'], TestData.card)

request_options = Shift4::RequestOptions.new(idempotency_key: random_idempotency_key.to_s)

# when
Shift4::Cards.update(customer['id'],
card['id'],
{
expMonth: '05',
expYear: '55',
cardholderName: 'updated cardholderName',
addressCountry: 'updated addressCountry',
addressCity: 'updated addressCity',
addressState: 'updated addressState',
addressZip: 'updated addressZip',
addressLine1: 'updated addressLine1',
addressLine2: 'updated addressLine2'
},
request_options)
not_updated_because_idempotency = Shift4::Cards.update(customer['id'],
card['id'],
{
expMonth: '05',
expYear: '55',
cardholderName: 'updated cardholderName',
addressCountry: 'updated addressCountry',
addressCity: 'updated addressCity',
addressState: 'updated addressState',
addressZip: 'updated addressZip',
addressLine1: 'updated addressLine1',
addressLine2: 'updated addressLine2'
},
request_options)

# then
expect(not_updated_because_idempotency.headers['Idempotent-Replayed']).to eq("true")
end

it 'delete card' do
# given
customer = Shift4::Customers.create(TestData.customer)
Expand Down
75 changes: 74 additions & 1 deletion spec/integration/charges_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,27 @@
expect(retrieved['card']['first4']).to eq(charge_req["card"]["first4"])
end

it 'create charge only once with idempotency_key' do
# given
charge_req = TestData.charge(card: TestData.card)
request_options = Shift4::RequestOptions.new(idempotency_key: random_idempotency_key.to_s)

# when
created = Shift4::Charges.create(charge_req, request_options)
not_created_because_idempotency = Shift4::Charges.create(charge_req, request_options)

# then
expect(created['id']).to eq(not_created_because_idempotency['id'])
expect(not_created_because_idempotency.headers['Idempotent-Replayed']).to eq("true")
end

it 'update charge' do
# given
card = TestData.card
charge_req = TestData.charge(card: card)
created = Shift4::Charges.create(charge_req)

# when
created = Shift4::Charges.create(charge_req)
updated = Shift4::Charges.update(created['id'],
"description" => "updated description",
"metadata" => { "key" => "updated value" })
Expand All @@ -62,6 +76,33 @@
expect(updated['card']['first4']).to eq(charge_req["card"]["first4"])
end

it 'update charge only once with idempotency_key' do
# given
card = TestData.card
charge_req = TestData.charge(card: card)
created = Shift4::Charges.create(charge_req)

request_options = Shift4::RequestOptions.new(idempotency_key: random_idempotency_key.to_s)

# when
Shift4::Charges.update(created['id'],
{
"description" => "updated description",
"metadata" => { "key" => "updated value" }
},
request_options)

not_updated_because_idempotency = Shift4::Charges.update(created['id'],
{
"description" => "updated description",
"metadata" => { "key" => "updated value" }
},
request_options)

# then
expect(not_updated_because_idempotency.headers['Idempotent-Replayed']).to eq("true")
end

it 'capture charge' do
# given
charge_req = TestData.charge(card: TestData.card, captured: false)
Expand All @@ -75,6 +116,22 @@
expect(captured['captured']).to eq(true)
end

it 'capture charge only once with idempotency_key' do
# given
charge_req = TestData.charge(card: TestData.card, captured: false)
created = Shift4::Charges.create(charge_req)

request_options = Shift4::RequestOptions.new(idempotency_key: random_idempotency_key.to_s)

# when
captured = Shift4::Charges.capture(created['id'], request_options)
not_captured_because_idempotency = Shift4::Charges.capture(created['id'], request_options)

# then
expect(captured['id']).to eq(not_captured_because_idempotency['id'])
expect(not_captured_because_idempotency.headers['Idempotent-Replayed']).to eq("true")
end

it 'refund charge' do
# given
charge_req = TestData.charge(card: TestData.card, captured: false)
Expand All @@ -88,6 +145,22 @@
expect(refunded['refunded']).to eq(true)
end

it 'refund charge only once with idempotency_key' do
# given
charge_req = TestData.charge(card: TestData.card, captured: false)
created = Shift4::Charges.create(charge_req)

request_options = Shift4::RequestOptions.new(idempotency_key: random_idempotency_key.to_s)

# when
refunded = Shift4::Charges.refund(created['id'], nil, request_options)
not_refunded_because_idempotency = Shift4::Charges.refund(created['id'], nil, request_options)

# then
expect(refunded['id']).to eq(not_refunded_because_idempotency['id'])
expect(not_refunded_because_idempotency.headers['Idempotent-Replayed']).to eq("true")
end

it 'list charges' do
# given
customer = Shift4::Customers.create(TestData.customer)
Expand Down
40 changes: 40 additions & 0 deletions spec/integration/credits_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@
expect(retrieved['card']['first4']).to eq(credit_req["card"]["first4"])
end

it 'create only once with idempotency_key' do
# given
credit_req = TestData.credit(card: TestData.card)
request_options = Shift4::RequestOptions.new(idempotency_key: random_idempotency_key.to_s)

# when
created = Shift4::Credits.create(credit_req, request_options)
not_created_because_idempotency = Shift4::Credits.create(credit_req, request_options)

# then
expect(created['id']).to eq(not_created_because_idempotency['id'])
expect(not_created_because_idempotency.headers['Idempotent-Replayed']).to eq("true")
end

it 'update credit' do
# given
card = TestData.card
Expand All @@ -42,6 +56,32 @@
expect(updated['card']['first4']).to eq(credit_req["card"]["first4"])
end

it 'update credit only once with idempotency_key' do
# given
card = TestData.card
credit_req = TestData.credit(card: card)
created = Shift4::Credits.create(credit_req)

request_options = Shift4::RequestOptions.new(idempotency_key: random_idempotency_key.to_s)

# when
Shift4::Credits.update(created['id'],
{
"description" => "updated description",
"metadata" => { "key" => "updated value" }
},
request_options)
not_updated_because_idempotency = Shift4::Credits.update(created['id'],
{
"description" => "updated description",
"metadata" => { "key" => "updated value" }
},
request_options)

# then
expect(not_updated_because_idempotency.headers['Idempotent-Replayed']).to eq("true")
end

it 'list credits' do
# given
customer = Shift4::Customers.create(TestData.customer)
Expand Down
34 changes: 34 additions & 0 deletions spec/integration/customers_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,20 @@
expect(retrieved['email']).to eq(customer_req['email'])
end

it 'create only once with idempotency_key' do
# given
customer_req = TestData.customer
request_options = Shift4::RequestOptions.new(idempotency_key: random_idempotency_key.to_s)

# when
created = Shift4::Customers.create(customer_req, request_options)
not_created_because_idempotency = Shift4::Customers.create(customer_req, request_options)

# then
expect(created['id']).to eq(not_created_because_idempotency['id'])
expect(not_created_because_idempotency.headers['Idempotent-Replayed']).to eq("true")
end

it 'update customer default card' do
# given
customer_req = TestData.customer(card: TestData.card)
Expand All @@ -32,6 +46,26 @@
expect(updated['defaultCardId']).to eq(new_card['id'])
end

it 'update customer only once with idempotency_key' do
# given
customer_req = TestData.customer(card: TestData.card)
customer = Shift4::Customers.create(customer_req)
new_card = Shift4::Cards.create(customer['id'], TestData.card)

request_options = Shift4::RequestOptions.new(idempotency_key: random_idempotency_key.to_s)

# when
Shift4::Customers.update(customer['id'],
{ defaultCardId: new_card['id'] },
request_options)
not_updated_because_idempotency = Shift4::Customers.update(customer['id'],
{ defaultCardId: new_card['id'] },
request_options)

# then
expect(not_updated_because_idempotency.headers['Idempotent-Replayed']).to eq("true")
end

it 'delete customer' do
# given
customer_req = TestData.customer(card: TestData.card)
Expand Down
Loading

0 comments on commit 2947d04

Please sign in to comment.