yookassa 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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