affirm-ruby-v1 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4d757343882a217c366a9d099221b8bfd1fb6febead45ece7d4c17150ff107de
4
+ data.tar.gz: 79954f618bff7277906768f8fff4a88ab574ad1aaf5220a2eecebb0f41cfccd2
5
+ SHA512:
6
+ metadata.gz: 4caaaed2deda060f25e2bfe1370e247cebfb998c833adb47e5dd5cf9671db77294ebdc4d7a331619c3cb35ba835027a6d2d5cafa66bd222a8934d32a69f17c8c
7
+ data.tar.gz: 6c7453b0a506718faeb054229facb3286d9b94e58896630379969c7661a69247ad7795890c2e596172029c7574f61fc6016de33300409cae4c684a5a9170d1f9
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/CHANGELOG.md ADDED
@@ -0,0 +1,21 @@
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [1.1.0][] (2026-05-27)
8
+
9
+ - Remove v2 compatibility shims; expose v1 response fields only
10
+ - Drop unused v2 object models from the gem load path
11
+
12
+ ## [1.0.0][] (2026-05-27)
13
+
14
+ - Initial release of affirm-ruby-v1
15
+ - Affirm `/api/v1/transactions` client
16
+ - Ruby 4.0 compatible dependencies
17
+ - Legacy response helpers for apps migrating from affirm-ruby v2 charges
18
+
19
+ [Unreleased]: https://github.com/hknaksu/affirm-ruby-v1/compare/v1.1.0...HEAD
20
+ [1.1.0]: https://github.com/hknaksu/affirm-ruby-v1/compare/v1.0.0...v1.1.0
21
+ [1.0.0]: https://github.com/hknaksu/affirm-ruby-v1/releases/tag/v1.0.0
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Yury Velikanau
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,113 @@
1
+ # affirm-ruby-v1
2
+
3
+ Ruby wrapper for the [Affirm Transactions API](https://docs.affirm.com/developers/docs/manage-transactions) (`/api/v1/transactions`).
4
+
5
+ ## Installation
6
+
7
+ ```ruby
8
+ gem "affirm-ruby-v1", require: "affirm"
9
+ ```
10
+
11
+ Local path:
12
+
13
+ ```ruby
14
+ gem "affirm-ruby-v1", path: "../affirm-ruby-v1", require: "affirm"
15
+ ```
16
+
17
+ ## Configuration
18
+
19
+ ```ruby
20
+ Affirm.configure do |config|
21
+ config.public_api_key = "ABC"
22
+ config.private_api_key = "XYZ"
23
+ config.environment = :sandbox # or :production
24
+ end
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ### Authorize
30
+
31
+ ```ruby
32
+ response = Affirm::Charge.authorize(checkout_token, order.id)
33
+ # or
34
+ response = Affirm::Charge.authorize(checkout_token, order_id: order.id.to_s)
35
+ ```
36
+
37
+ ### Read transaction
38
+
39
+ ```ruby
40
+ Affirm::Charge.find(transaction_id)
41
+ ```
42
+
43
+ ### Capture
44
+
45
+ ```ruby
46
+ Affirm::Charge.capture(transaction_id)
47
+ Affirm::Charge.capture(transaction_id, amount: 5000) # partial capture
48
+ ```
49
+
50
+ ### Void
51
+
52
+ ```ruby
53
+ Affirm::Charge.void(transaction_id)
54
+ ```
55
+
56
+ ### Refund
57
+
58
+ ```ruby
59
+ Affirm::Charge.refund(transaction_id, amount: 500)
60
+ ```
61
+
62
+ ### Update
63
+
64
+ ```ruby
65
+ Affirm::Charge.update(transaction_id,
66
+ order_id: "CUSTOM_ORDER_ID",
67
+ shipping_carrier: "USPS",
68
+ shipping_confirmation: "1Z23223"
69
+ )
70
+ ```
71
+
72
+ ### Response handling
73
+
74
+ ```ruby
75
+ if response.success?
76
+ response.id # Affirm transaction id (e.g. "AMLC-5X0W")
77
+ response.status # e.g. "authorized"
78
+ response.amount
79
+ response.checkout_id
80
+ response.order_id
81
+ response.authorization_expiration
82
+ else
83
+ response.error.message
84
+ end
85
+ ```
86
+
87
+ ## v1 authorize response fields
88
+
89
+ | Field | Type | Description |
90
+ |-------|------|-------------|
91
+ | `id` | String | Transaction id — store this for capture/void/refund |
92
+ | `status` | String | e.g. `authorized`, `captured`, `voided` |
93
+ | `amount` | Integer | Amount in cents |
94
+ | `currency` | String | e.g. `USD` |
95
+ | `checkout_id` | String | Affirm checkout identifier |
96
+ | `order_id` | String | Your merchant order id |
97
+ | `authorization_expiration` | DateTime | When the authorization expires |
98
+ | `amount_refunded` | Integer | Refunded amount in cents |
99
+ | `events` | Array | Auth/capture/refund event history |
100
+
101
+ ## Contributing
102
+
103
+ 1. Fork the repository
104
+ 2. Create your feature branch
105
+ 3. Commit your changes
106
+ 4. Push to the branch
107
+ 5. Open a pull request
108
+
109
+ ## License
110
+
111
+ Released under the MIT license which is included in the [MIT-LICENSE](https://github.com/kjvarga/sitemap_generator/blob/master/MIT-LICENSE) file.
112
+
113
+ Copyright (c) Hakan Aksu
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ desc "Run specs"
5
+ RSpec::Core::RakeTask.new("spec") do |task|
6
+ task.verbose = false
7
+ end
8
+
9
+ desc "Console"
10
+ task :console do
11
+ require "irb"
12
+ require "irb/completion"
13
+ require "affirm"
14
+ require "byebug"
15
+ ARGV.clear
16
+ IRB.start
17
+ end
18
+
19
+ task default: :spec
@@ -0,0 +1,63 @@
1
+ module Affirm
2
+ module Charge
3
+ extend self
4
+
5
+ # Authorize a checkout token against the Affirm v1 Transactions API.
6
+ #
7
+ # Affirm::Charge.authorize(checkout_token, order.id)
8
+ # Affirm::Charge.authorize(checkout_token, order.id, reference_id: "REF-1")
9
+ #
10
+ def authorize(checkout_token, order_id, reference_id: nil)
11
+ if order_id.nil? || order_id.to_s.empty?
12
+ raise ArgumentError, "order_id is required for Affirm v1 authorize"
13
+ end
14
+
15
+ payload = {
16
+ transaction_id: checkout_token,
17
+ order_id: order_id.to_s
18
+ }
19
+ payload[:reference_id] = reference_id if reference_id
20
+
21
+ respond Client.request(:post, "transactions", **payload)
22
+ end
23
+
24
+ def find(transaction_id)
25
+ respond Client.request(:get, "transactions/#{transaction_id}")
26
+ end
27
+
28
+ def capture(transaction_id, **options)
29
+ respond Client.request(:post, "transactions/#{transaction_id}/capture", **options)
30
+ end
31
+
32
+ def void(transaction_id)
33
+ respond Client.request(:post, "transactions/#{transaction_id}/void")
34
+ end
35
+
36
+ def refund(transaction_id, amount:)
37
+ respond Client.request(:post, "transactions/#{transaction_id}/refund", amount: amount)
38
+ end
39
+
40
+ def update(transaction_id, **updates)
41
+ respond Client.request(:post, "transactions/#{transaction_id}/update", **updates)
42
+ end
43
+
44
+ private
45
+
46
+ def respond(response)
47
+ return FailureResult.new(response) unless response.success?
48
+
49
+ body = response.body
50
+ return FailureResult.new(response) unless body.is_a?(Hash)
51
+
52
+ type = body["type"]
53
+ klass = case type
54
+ when *%w(capture void refund update)
55
+ Object.const_get("Affirm::Responses::#{type.capitalize}")
56
+ else
57
+ Affirm::Responses::Auth
58
+ end
59
+
60
+ SuccessResult.new(klass.new(body))
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,50 @@
1
+ module Affirm
2
+ class Client
3
+ attr_reader :connection
4
+
5
+ attr_reader :url_prefix
6
+ private :url_prefix
7
+
8
+ class << self
9
+ def request(method, path, data={})
10
+ new.public_send(method, path, data)
11
+ end
12
+ end
13
+
14
+ def initialize
15
+ @url_prefix = "/api/v1"
16
+ @connection = Faraday.new(Affirm.configuration.endpoint) do |conn|
17
+ conn.request :authorization, :basic, basic_auth_user, basic_auth_password
18
+ conn.request :json
19
+ conn.response :json, content_type: /\bjson$/
20
+ conn.adapter Faraday.default_adapter
21
+ end
22
+ end
23
+
24
+ def get(path, data={})
25
+ connection.get(normalized_path(path), data)
26
+ end
27
+
28
+ def post(path, data={})
29
+ connection.post(normalized_path(path), data)
30
+ end
31
+
32
+ private
33
+
34
+ def basic_auth_user
35
+ Affirm.configuration.public_api_key
36
+ end
37
+
38
+ def basic_auth_password
39
+ Affirm.configuration.private_api_key
40
+ end
41
+
42
+ def normalized_path(path)
43
+ url_prefix + normalize_path(path)
44
+ end
45
+
46
+ def normalize_path(path)
47
+ Faraday::Utils.normalize_path(path)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,38 @@
1
+ module Affirm
2
+ class Configuration
3
+ attr_accessor :public_api_key
4
+ attr_accessor :private_api_key
5
+ attr_reader :environment
6
+
7
+ ENDPOINTS = {
8
+ production: "api.affirm.com",
9
+ sandbox: "sandbox.affirm.com"
10
+ }
11
+
12
+ def initialize
13
+ @environment = :production
14
+ end
15
+
16
+ def environment=(env)
17
+ @environment = env.to_sym
18
+ end
19
+
20
+ def endpoint
21
+ "https://#{ENDPOINTS[environment]}"
22
+ end
23
+ end
24
+
25
+ class << self
26
+ def configuration
27
+ @configuration ||= Configuration.new
28
+ end
29
+
30
+ def configuration=(config)
31
+ @configuration = config
32
+ end
33
+
34
+ def configure
35
+ yield configuration
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,18 @@
1
+ module Affirm
2
+ class FailureResult
3
+ extend Forwardable
4
+ def_delegators :response, :status, :success?
5
+
6
+ attr_reader :error
7
+
8
+ attr_reader :response
9
+ private :response
10
+
11
+ def initialize(response)
12
+ @response = response
13
+ body = response.body
14
+ body = { "message" => body.to_s } unless body.is_a?(Hash)
15
+ @error = Affirm::Responses::Error.new(body)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ module Affirm
2
+ module Objects
3
+ class Event
4
+ include Virtus.model
5
+
6
+ attribute :id, String
7
+ attribute :transaction_id, String
8
+ attribute :currency, String
9
+ attribute :amount, Integer
10
+ attribute :type, String
11
+ attribute :created, DateTime
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ module Affirm
2
+ module Responses
3
+ class Auth
4
+ include Virtus.model
5
+
6
+ attribute :id, String
7
+ attribute :status, String
8
+ attribute :amount, Integer
9
+ attribute :amount_refunded, Integer
10
+ attribute :authorization_expiration, DateTime
11
+ attribute :checkout_id, String
12
+ attribute :created, DateTime
13
+ attribute :currency, String
14
+ attribute :events, Array[Objects::Event]
15
+ attribute :order_id, String
16
+ attribute :provider_id, Integer
17
+ attribute :remove_tax, Boolean
18
+ attribute :reference_id, String
19
+ attribute :tax_amount, Integer
20
+ attribute :shipping_amount, Integer
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ module Affirm
2
+ module Responses
3
+ class Capture
4
+ include Virtus.model
5
+
6
+ attribute :id, String
7
+ attribute :type, String
8
+ attribute :amount, Integer
9
+ attribute :currency, String
10
+ attribute :fee, Integer
11
+ attribute :created, DateTime
12
+ attribute :order_id, String
13
+ attribute :reference_id, String
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,13 @@
1
+ module Affirm
2
+ module Responses
3
+ class Error
4
+ include Virtus.model
5
+
6
+ attribute :status_code, Integer
7
+ attribute :type, String
8
+ attribute :code, String
9
+ attribute :message, String
10
+ attribute :field, String
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ module Affirm
2
+ module Responses
3
+ class Refund
4
+ include Virtus.model
5
+
6
+ attribute :id, String
7
+ attribute :reference_id, String
8
+ attribute :amount, Integer
9
+ attribute :fee_refunded, Integer
10
+ attribute :created, DateTime
11
+ attribute :type, String
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module Affirm
2
+ module Responses
3
+ class Update
4
+ include Virtus.model
5
+
6
+ attribute :id, String
7
+ attribute :order_id, String
8
+ attribute :shipping_carrier, String
9
+ attribute :shipping_confirmation, String
10
+ attribute :created, DateTime
11
+ attribute :type, String
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ module Affirm
2
+ module Responses
3
+ class Void
4
+ include Virtus.model
5
+
6
+ attribute :id, String
7
+ attribute :type, String
8
+ attribute :created, DateTime
9
+ attribute :reference_id, String
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,26 @@
1
+ module Affirm
2
+ class SuccessResult
3
+ attr_reader :object
4
+ private :object
5
+
6
+ def initialize(object)
7
+ @object = object
8
+ end
9
+
10
+ def success?
11
+ true
12
+ end
13
+
14
+ def method_missing(method_name, *arguments, &block)
15
+ if object.respond_to?(method_name)
16
+ object.send(method_name, *arguments, &block)
17
+ else
18
+ super
19
+ end
20
+ end
21
+
22
+ def respond_to_missing?(method_name, include_private = false)
23
+ object.respond_to?(method_name) || super
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ module Affirm
2
+ VERSION = "1.1.0"
3
+ end
data/lib/affirm.rb ADDED
@@ -0,0 +1,19 @@
1
+ require "forwardable"
2
+
3
+ require "faraday"
4
+ require "virtus"
5
+
6
+ require "affirm/client"
7
+ require "affirm/charge"
8
+ require "affirm/configuration"
9
+ require "affirm/success_result"
10
+ require "affirm/failure_result"
11
+ require "affirm/objects/event"
12
+ require "affirm/responses/auth"
13
+ require "affirm/responses/capture"
14
+ require "affirm/responses/void"
15
+ require "affirm/responses/refund"
16
+ require "affirm/responses/update"
17
+ require "affirm/responses/error"
18
+
19
+ require "affirm/version"
@@ -0,0 +1,45 @@
1
+ RSpec.shared_examples "a transaction object interface" do
2
+ %w(
3
+ id
4
+ status
5
+ currency
6
+ amount
7
+ order_id
8
+ checkout_id
9
+ ).each do |method|
10
+ it method do
11
+ expect(subject.public_send(method)).to eq(body[method])
12
+ end
13
+ end
14
+
15
+ it "created" do
16
+ expect(
17
+ to_seconds(subject.created)
18
+ ).to eq(to_seconds(body["created"]))
19
+ end
20
+
21
+ it "authorization_expiration" do
22
+ expect(
23
+ to_seconds(subject.authorization_expiration)
24
+ ).to eq(to_seconds(body["authorization_expiration"]))
25
+ end
26
+
27
+ %w(
28
+ id
29
+ currency
30
+ amount
31
+ type
32
+ ).each do |method|
33
+ it "events.#{method}" do
34
+ expect(
35
+ subject.events.first.public_send(method)
36
+ ).to eq(body["events"].first[method])
37
+ end
38
+ end
39
+
40
+ it "events.created" do
41
+ expect(
42
+ to_seconds(subject.events.first.created)
43
+ ).to eq(to_seconds(body["events"].first["created"]))
44
+ end
45
+ end