yookassa 0.1.0 → 0.2.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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +31 -0
  3. data/.rubocop.yml +16 -9
  4. data/Gemfile +10 -3
  5. data/Gemfile.lock +108 -69
  6. data/README.md +43 -14
  7. data/Rakefile +7 -1
  8. data/bin/console +13 -0
  9. data/bin/rake +29 -0
  10. data/bin/rspec +29 -0
  11. data/bin/rubocop +29 -0
  12. data/bin/setup +8 -0
  13. data/lib/yookassa/client.rb +44 -0
  14. data/lib/yookassa/config.rb +7 -0
  15. data/lib/yookassa/entity/amount.rb +5 -7
  16. data/lib/yookassa/entity/authorization_details.rb +28 -0
  17. data/lib/yookassa/entity/cancellation_details.rb +18 -0
  18. data/lib/yookassa/entity/card.rb +38 -11
  19. data/lib/yookassa/entity/confirmation.rb +7 -9
  20. data/lib/yookassa/entity/error.rb +19 -0
  21. data/lib/yookassa/entity/payment.rb +110 -14
  22. data/lib/yookassa/entity/payment_methods.rb +107 -0
  23. data/lib/yookassa/entity/recipient.rb +12 -0
  24. data/lib/yookassa/entity/refund.rb +11 -7
  25. data/lib/yookassa/entity/transfer.rb +33 -0
  26. data/lib/yookassa/entity/types.rb +9 -0
  27. data/lib/yookassa/payments.rb +35 -0
  28. data/lib/yookassa/refunds.rb +25 -0
  29. data/lib/yookassa/version.rb +1 -1
  30. data/lib/yookassa.rb +29 -11
  31. data/spec/fixtures/refund.json +4 -4
  32. data/spec/spec_helper.rb +3 -8
  33. data/spec/yookassa/config_spec.rb +7 -0
  34. data/spec/yookassa/payments_spec.rb +101 -0
  35. data/spec/yookassa/refunds_spec.rb +54 -0
  36. data/spec/yookassa_spec.rb +38 -1
  37. data/yookassa.gemspec +13 -18
  38. metadata +36 -82
  39. data/.circleci/config.yml +0 -28
  40. data/lib/yookassa/callable.rb +0 -10
  41. data/lib/yookassa/entity/payment_method.rb +0 -19
  42. data/lib/yookassa/error.rb +0 -30
  43. data/lib/yookassa/optional.rb +0 -23
  44. data/lib/yookassa/payment.rb +0 -65
  45. data/lib/yookassa/refund.rb +0 -35
  46. data/lib/yookassa/response.rb +0 -20
  47. data/spec/yookassa/payment_spec.rb +0 -104
  48. data/spec/yookassa/refund_spec.rb +0 -54
@@ -1,18 +1,45 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "./types"
4
+
3
5
  module Yookassa
4
6
  module Entity
5
- class Card
6
- extend Dry::Initializer
7
- extend Yookassa::Callable
8
- include Yookassa::Optional
9
-
10
- option :first6
11
- option :last4
12
- option :expiry_month
13
- option :expiry_year
14
- option :card_type, proc(&:to_s)
15
- option :source, proc(&:to_s), optional: true
7
+ class Card < Dry::Struct
8
+ # first6 [string, optional]
9
+ # First 6 digits of the card’s number (BIN). For payments with bank cards saved in YooMoney
10
+ # and other services, the specified BIN might not correspond with the last4, expiry_year, expiry_month values.
11
+ # For payments with bank cards saved in Apple Pay or Google Pay, the parameter contains Device Account Number.
12
+ attribute? :first6, Types::String
13
+
14
+ # last4 [string, required]
15
+ # Last 4 digits of the card's number.
16
+ attribute :last4, Types::String
17
+
18
+ # expiry_month [string, required]
19
+ # Expiration date, month, MM.
20
+ attribute :expiry_month, Types::Coercible::Integer
21
+
22
+ # expiry_year [string, required]
23
+ # Expiration date, year, YYYY.
24
+ attribute :expiry_year, Types::Coercible::Integer
25
+
26
+ # card_type [string, required]
27
+ # Type of bank card. Possible values: MasterCard (for Mastercard and Maestro cards), Visa (for Visa and Visa Electron cards),
28
+ # Mir, UnionPay, JCB, AmericanExpress, DinersClub, and Unknown.
29
+ attribute :card_type, Types::String.enum("MasterCard", "Visa", "Mir", "UnionPay", "JCB", "AmericanExpress", "DinersClub", "Unknown")
30
+
31
+ # issuer_country [string, optional]
32
+ # Code of the country where the bank card was issued according to ISO-3166 alpha-2. Example: RU.
33
+ attribute? :issuer_country, Types::String
34
+
35
+ # issuer_name [string, optional]
36
+ # Name of the issuing bank.
37
+ attribute? :issuer_name, Types::String
38
+
39
+ # source [string, optional]
40
+ # Source of bank card details. Possible values: apple_pay, google_pay.
41
+ # For payments where the user selects a card saved in Apple Pay or Google Pay.
42
+ attribute? :source, Types::String.enum("apple_pay", "google_pay")
16
43
  end
17
44
  end
18
45
  end
@@ -1,16 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "./types"
4
+
3
5
  module Yookassa
4
6
  module Entity
5
- class Confirmation
6
- extend Dry::Initializer
7
- extend Yookassa::Callable
8
- include Yookassa::Optional
9
-
10
- option :type, proc(&:to_s), optional: true
11
- option :confirmation_url, proc(&:to_s), optional: true
12
- option :enforce, optional: true
13
- option :return_url, proc(&:to_s), optional: true
7
+ class Confirmation < Dry::Struct
8
+ attribute :type, Types::String
9
+ attribute :confirmation_url, Types::String
10
+ attribute? :enforce, Types::Bool
11
+ attribute? :return_url, Types::String
14
12
  end
15
13
  end
16
14
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./types"
4
+
5
+ module Yookassa
6
+ module Entity
7
+ class Error < Dry::Struct
8
+ attribute :type, Types::String
9
+ attribute? :id, Types::String
10
+ attribute? :code, Types::String
11
+ attribute? :description, Types::String
12
+ attribute? :parameter, Types::String
13
+
14
+ def error?
15
+ type == "error"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,22 +1,118 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './amount'
4
- require_relative './payment_method'
5
- require_relative './confirmation'
3
+ require_relative "./types"
4
+ require_relative "./amount"
5
+ require_relative "./payment_methods"
6
+ require_relative "./confirmation"
7
+ require_relative "./recipient"
8
+ require_relative "./cancellation_details"
9
+ require_relative "./authorization_details"
10
+ require_relative "./transfer"
6
11
 
7
12
  module Yookassa
8
13
  module Entity
9
- class Payment < Yookassa::Response
10
- option :paid
11
- option :amount, Entity::Amount
12
- option :created_at
13
- option :captured_at, proc(&:to_s), optional: true
14
- option :expires_at, optional: true
15
- option :description, proc(&:to_s), optional: true
16
- option :metadata, optional: true
17
- option :payment_method, Entity::PaymentMethod, optional: true
18
- option :confirmation, Entity::Confirmation, optional: true
19
- option :test
14
+ class Payment < Dry::Struct
15
+ # id [string, required]
16
+ # Payment ID in YooMoney.
17
+ attribute :id, Types::String
18
+
19
+ attribute? :idempotency_key, Types::String
20
+
21
+ # status [string, required]
22
+ # Payment status. Possible values: pending, waiting_for_capture, succeeded, and canceled.
23
+ # More about the life cycle of a payment https://yookassa.ru/en/developers/api#:~:text=life%20cycle%20of%20a%20payment%C2%A0
24
+ attribute :status, Types::String.enum("pending", "waiting_for_capture", "succeeded", "canceled")
25
+
26
+ # amount [object, required]
27
+ # Payment amount. Sometimes YooMoney's partners charge additional commission from the users that is not included in this amount.
28
+ attribute :amount, Entity::Amount
29
+
30
+ # income_amount [object, optional]
31
+ # Amount of payment to be received by the store: the amount value minus the YooMoney commission.
32
+ # If you're a partner using an OAuth token for request authentication, make a request to the store for a right
33
+ # to get information about commissions on payments.
34
+ attribute? :income_amount, Entity::Amount
35
+
36
+ # description [string, optional]
37
+ # Description of the transaction (maximum 128 characters) displayed in your YooMoney Merchant Profile,
38
+ # and shown to the user during checkout. For example, "Payment for order No. 72 for user@yoomoney.ru".
39
+ attribute? :description, Types::String.constrained(max_size: 128)
40
+
41
+ # recipient [object, required]
42
+ # Payment recipient.
43
+ attribute :recipient, Entity::Recipient
44
+
45
+ # payment_method [object, optional]
46
+ # Payment method used for this payment.
47
+ attribute? :payment_method, Entity::PaymentMethods
48
+
49
+ # captured_at [datetime, optional]
50
+ # Time of payment capture, based on UTC and specified in the ISO 8601 format. "2018-07-18T10:51:18.139Z"
51
+ attribute? :captured_at, Types::JSON::DateTime
52
+
53
+ # created_at [datetime, required]
54
+ # Time of order creation, based on UTC and specified in the ISO 8601 format. Example: 2017-11-03T11:52:31.827Z
55
+ attribute :created_at, Types::JSON::DateTime
56
+
57
+ # expires_at [string, optional]
58
+ # The period during which you can cancel or capture a payment for free. The payment with the waiting_for_capture status
59
+ # will be automatically canceled at the specified time. Based on UTC and specified in the ISO 8601 format.
60
+ attribute? :expires_at, Types::JSON::DateTime
61
+
62
+ # confirmation [object, optional]
63
+ # Selected payment confirmation scenario. For payments requiring confirmation from the user.
64
+ # More about confirmation scenarios https://yookassa.ru/en/developers/api#:~:text=confirmation,from%20the%20user.%20More%20about%20confirmation%20scenarios%C2%A0
65
+ attribute? :confirmation, Entity::Confirmation
66
+
67
+ # test [boolean, required]
68
+ # The attribute of a test transaction.
69
+ attribute :test, Types::Bool
70
+
71
+ # refunded_amount [object, optional]
72
+ # The amount refunded to the user. Specified if the payment has successful refunds.
73
+ attribute? :refunded_amount, Entity::Amount
74
+
75
+ # paid [boolean, required]
76
+ # The attribute of a paid order.
77
+ attribute :paid, Types::Bool
78
+
79
+ # refundable [boolean, required]
80
+ # Availability of the option to make a refund via API.
81
+ attribute :refundable, Types::Bool
82
+
83
+ # receipt_registration [string, optional]
84
+ # Delivery status of receipt data to online sales register (pending, succeeded, or canceled). For those who use the solution for 54-FZ .
85
+ attribute? :receipt_registration, Types::String.enum("pending", "succeeded", "canceled")
86
+
87
+ # metadata [object, optional]
88
+ # Any additional data you might require for processing payments (for example, order number), specified as a “key-value” pair
89
+ # and returned in response from YooMoney.
90
+ # Limitations:
91
+ # - no more than 16 keys,
92
+ # - no more than 32 characters in the key’s title,
93
+ # - no more than 512 characters in the key’s value,
94
+ # - data type is a string in the UTF-8 format.
95
+ attribute? :metadata, Types::Hash
96
+
97
+ # cancellation_details [object, optional]
98
+ # Commentary to the canceled status: who and why canceled the payment.
99
+ # More about canceled payments https://yookassa.ru/en/developers/api#:~:text=cancellation_details,about%20canceled%20payments%C2%A0
100
+ attribute? :cancellation_details, Entity::CancellationDetails
101
+
102
+ # authorization_details [object, optional]
103
+ # Payment authorization details.
104
+ attribute? :authorization_details, Entity::AuthorizationDetails
105
+
106
+ # transfers [array, optional]
107
+ # Information about money distribution: the amounts of transfers and the stores to be transferred to.
108
+ # Specified if you use the YooMoney solution for marketplaces https://yookassa.ru/en/developers/special-solutions/checkout-for-platforms/basics
109
+ attribute? :transfers, Types::Array.of(Entity::Transfer)
110
+
111
+ # merchant_customer_id [string, optional]
112
+ # The identifier of the customer in your system, such as email address or phone number. No more than 200 characters.
113
+ # Specified if you want to save a bank card and offer it for a recurring payment
114
+ # in the YooMoney payment widget https://yookassa.ru/en/developers/payment-forms/widget/basics.
115
+ attribute? :merchant_customer_id, Types::String
20
116
  end
21
117
  end
22
118
  end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./types"
4
+ require_relative "./card"
5
+
6
+ module Yookassa
7
+ module Entity
8
+ module PaymentMethod
9
+ class Base < Dry::Struct
10
+ # id [string, required]
11
+ # Payment method ID.
12
+ attribute :id, Types::String
13
+
14
+ # saved [boolean, required]
15
+ # Saving payment methods allows conducting automatic recurring payments .
16
+ attribute :saved, Types::Bool
17
+
18
+ # title [string, optional]
19
+ # Payment method name.
20
+ attribute? :title, Types::String
21
+ end
22
+
23
+ class BankCard < Base
24
+ attribute :type, Types.Value("bank_card")
25
+
26
+ # login [string, optional]
27
+ # User's login in Alfa-Click (linked phone number or the additional login).
28
+ attribute? :card, Entity::Card
29
+ end
30
+
31
+ class Alfabank < Base
32
+ attribute :type, Types.Value("alfabank")
33
+
34
+ # login [string, optional]
35
+ # User's login in Alfa-Click (linked phone number or the additional login).
36
+ attribute? :login, Types::String
37
+ end
38
+
39
+ class YooMoney < Base
40
+ attribute :type, Types.Value("yoo_money")
41
+
42
+ # account_number [string, optional]
43
+ # The number of the YooMoney wallet used for making the payment.
44
+ attribute? :account_number, Types::String
45
+ end
46
+
47
+ class Sberbank < Base
48
+ attribute :type, Types.Value("sberbank")
49
+
50
+ # phone [string, optional]
51
+ # TThe phone number specified during the registration process of the SberBank Online/SberPay account,
52
+ # specified in the ITU-T E.164 format, for example, 79000000000.
53
+ attribute? :phone, Types::String
54
+ end
55
+
56
+ class ApplePay < Base
57
+ attribute :type, Types.Value("apple_pay")
58
+ end
59
+
60
+ class Cash < Base
61
+ attribute :type, Types.Value("cash")
62
+ end
63
+
64
+ class DirectCarrierBilling < Base
65
+ attribute :type, Types.Value("mobile_balance")
66
+ end
67
+
68
+ class GooglePay < Base
69
+ attribute :type, Types.Value("google_pay")
70
+ end
71
+
72
+ class Installments < Base
73
+ attribute :type, Types.Value("installments")
74
+ end
75
+
76
+ class Qiwi < Base
77
+ attribute :type, Types.Value("qiwi")
78
+ end
79
+
80
+ class Tinkoff < Base
81
+ attribute :type, Types.Value("tinkoff_bank")
82
+ end
83
+
84
+ class Wechat < Base
85
+ attribute :type, Types.Value("wechat")
86
+ end
87
+
88
+ class Webmoney < Base
89
+ attribute :type, Types.Value("webmoney")
90
+ end
91
+ end
92
+
93
+ PaymentMethods = PaymentMethod::BankCard |
94
+ PaymentMethod::Alfabank |
95
+ PaymentMethod::YooMoney |
96
+ PaymentMethod::Sberbank |
97
+ PaymentMethod::ApplePay |
98
+ PaymentMethod::Cash |
99
+ PaymentMethod::DirectCarrierBilling |
100
+ PaymentMethod::GooglePay |
101
+ PaymentMethod::Installments |
102
+ PaymentMethod::Qiwi |
103
+ PaymentMethod::Tinkoff |
104
+ PaymentMethod::Wechat |
105
+ PaymentMethod::Webmoney
106
+ end
107
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./types"
4
+
5
+ module Yookassa
6
+ module Entity
7
+ class Recipient < Dry::Struct
8
+ attribute :account_id, Types::String
9
+ attribute :gateway_id, Types::String
10
+ end
11
+ end
12
+ end
@@ -1,15 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './amount'
3
+ require_relative "./types"
4
+ require_relative "./amount"
4
5
 
5
6
  module Yookassa
6
7
  module Entity
7
- class Refund < Yookassa::Response
8
- option :payment_id
9
- option :created_at, proc(&:to_s)
10
- option :amount, Entity::Amount
11
- option :receipt_registration, proc(&:to_s), optional: true
12
- option :description, proc(&:to_s), optional: true
8
+ class Refund < Dry::Struct
9
+ attribute :id, Types::String
10
+ attribute? :idempotency_key, Types::String
11
+ attribute :status, Types::String
12
+ attribute :payment_id, Types::String
13
+ attribute :created_at, Types::String
14
+ attribute :amount, Entity::Amount
15
+ attribute? :receipt_registration, Types::String
16
+ attribute :description, Types::String
13
17
  end
14
18
  end
15
19
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./types"
4
+ require_relative "./amount"
5
+
6
+ module Yookassa
7
+ module Entity
8
+ class Transfer < Dry::Struct
9
+ # account_id [string, required]
10
+ # ID of the store in favor of which you're accepting the receipt. Provided by YooMoney, displayed in the Sellers section
11
+ # of your Merchant Profile (shopId column).
12
+ attribute :account_id, Types::String
13
+
14
+ # amount [object, required]
15
+ # Amount to be transferred to the store.
16
+ attribute :amount, Entity::Amount
17
+
18
+ # status [string, required]
19
+ # Status of the money distribution between stores. Possible values: pending, waiting_for_capture, succeeded, canceled.
20
+ attribute :status, Types::String.enum("pending", "waiting_for_capture", "succeeded", "canceled")
21
+
22
+ # platform_fee_amount [object, optional]
23
+ # Commission for sold products or services charged in your favor.
24
+ attribute? :platform_fee_amount, Entity::Amount
25
+
26
+ # metadata [object, optional]
27
+ # Any additional data you might require for processing payments (for example, order number), specified as a “key-value” pair
28
+ # and returned in response from YooMoney. Limitations: no more than 16 keys, no more than 32 characters in the key’s title,
29
+ # no more than 512 characters in the key’s value, data type is a string in the UTF-8 format.
30
+ attribute? :metadata, Types::Hash
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yookassa
4
+ module Entity
5
+ module Types
6
+ include Dry.Types()
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./entity/payment"
4
+
5
+ module Yookassa
6
+ class Payments
7
+ def initialize(api)
8
+ @api = api
9
+ end
10
+
11
+ def find(payment_id:)
12
+ data = api.get("payments/#{payment_id}")
13
+ Entity::Payment.new(**data)
14
+ end
15
+
16
+ def create(payment:, idempotency_key: SecureRandom.hex(10))
17
+ data = api.post("payments", payload: payment, idempotency_key: idempotency_key)
18
+ Entity::Payment.new(**data.merge(idempotency_key: idempotency_key))
19
+ end
20
+
21
+ def capture(payment_id:, idempotency_key: SecureRandom.hex(10))
22
+ data = api.post("payments/#{payment_id}/capture", idempotency_key: idempotency_key)
23
+ Entity::Payment.new(**data.merge(idempotency_key: idempotency_key))
24
+ end
25
+
26
+ def cancel(payment_id:, idempotency_key: SecureRandom.hex(10))
27
+ data = api.post("payments/#{payment_id}/cancel", idempotency_key: idempotency_key)
28
+ Entity::Payment.new(**data.merge(idempotency_key: idempotency_key))
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :api
34
+ end
35
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./entity/refund"
4
+
5
+ module Yookassa
6
+ class Refunds
7
+ def initialize(api)
8
+ @api = api
9
+ end
10
+
11
+ def find(payment_id:)
12
+ data = api.get("refunds/#{payment_id}")
13
+ Entity::Refund.new(**data)
14
+ end
15
+
16
+ def create(payload:, idempotency_key: SecureRandom.hex(10))
17
+ data = api.post("refunds", payload: payload, idempotency_key: idempotency_key)
18
+ Entity::Refund.new(**data.merge(idempotency_key: idempotency_key))
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :api
24
+ end
25
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Yookassa
4
- VERSION = '0.1.0'
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/yookassa.rb CHANGED
@@ -1,13 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'evil/client'
4
-
5
- require 'yookassa/version'
6
- require 'yookassa/payment'
7
- require 'yookassa/refund'
8
- require 'yookassa/response'
9
- require 'yookassa/callable'
10
- require 'yookassa/optional'
11
- require 'yookassa/entity/payment'
12
- require 'yookassa/entity/refund'
13
- require 'yookassa/error'
3
+ require "dry-struct"
4
+ require "forwardable"
5
+ require "yookassa/version"
6
+ require "yookassa/config"
7
+ require "yookassa/client"
8
+
9
+ module Yookassa
10
+ ConfigError = Class.new(StandardError)
11
+
12
+ class << self
13
+ extend Forwardable
14
+
15
+ def configure
16
+ yield(config)
17
+ end
18
+
19
+ def config
20
+ @config ||= Config.new
21
+ end
22
+
23
+ def client
24
+ raise ConfigError, "Specify `shop_id` and `api_key` settins in a `.configure` block" if @config.nil?
25
+
26
+ @client ||= Client.new(shop_id: @config.shop_id, api_key: @config.api_key)
27
+ end
28
+
29
+ def_delegators :client, :payments, :refunds
30
+ end
31
+ end
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "amount": {
3
- "value": "8.00",
4
- "currency": "RUB"
5
- },
6
- "payment_id": "40484c25-1095-4d36-8b17-92672d1e127d",
3
+ "value": "8.00",
4
+ "currency": "RUB"
5
+ },
6
+ "payment_id": "40484c25-1095-4d36-8b17-92672d1e127d",
7
7
  "description": "test refund, idem-key 40484c25-1095-4d36-8b17-92672d1e127d"
8
8
  }
data/spec/spec_helper.rb CHANGED
@@ -1,21 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  begin
4
- require 'pry'
4
+ require "pry"
5
5
  rescue LoadError
6
6
  nil
7
7
  end
8
8
 
9
- require 'yookassa'
10
- require 'webmock/rspec'
9
+ require "yookassa"
10
+ require "webmock/rspec"
11
11
 
12
12
  RSpec.configure do |config|
13
13
  config.order = :random
14
14
  config.filter_run focus: true
15
15
  config.run_all_when_everything_filtered = true
16
-
17
- config.around(:each) do |example|
18
- stub_request(:any, //)
19
- example.run
20
- end
21
16
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Yookassa::Config do
4
+ subject { described_class.new }
5
+ it { is_expected.to respond_to(:shop_id) }
6
+ it { is_expected.to respond_to(:api_key) }
7
+ end