pay 2.6.3 → 2.6.8

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of pay might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 429e92c76a90f5de0cec18dce219479de7ea172d6807c3b0d86d0a2fe4994b77
4
- data.tar.gz: 85b9e46b65d4ea0fa0e7255cc47e1e5c60e55e14f18fee9c567c4befb0e1dac7
3
+ metadata.gz: d2e815585d6d3e171b89f184781db67eafddbd7d10ec8e5635b1d6e53b6475ab
4
+ data.tar.gz: 178f9466b98867c2bbaaa16047f78d5a55f35f4056bc964cd39d9bc29023c1d6
5
5
  SHA512:
6
- metadata.gz: 577c6792e5ff3f60e01a02de38bdc414aa0c6178ec0927c6b5766051224f632f83d551a787888d196ac57cf7d4299786a5f1afbcfc0376b10b9115ca97c74dc7
7
- data.tar.gz: dc0da5b5df00f672cc621acf89168f7b3c8c1f419aaa7c5802f015cfb3a4c6cf95284310fc776ca67e51da69c803a24d9c66b50407bd08044492abff1444eb00
6
+ metadata.gz: fb149c7f58a970019d4f65aa1d461e62bbc3d58a8478f715d9f8f3b858accaa6b9d12e951ea159472bc087635040601ab95d3a270bc9f544c777951f6964e513
7
+ data.tar.gz: efb10c4e80ea59dda1b0f9d24a835dcbfebb91feb13a0e882b9acf0ca6d446d1930369c922ef5353562bdf1c7690a5702f6ea81b27a667743fccadcaa2d541ca
data/README.md CHANGED
@@ -1,8 +1,10 @@
1
- <p align="center"><img src="docs/logo.svg" height="50px"></p>
1
+ <p align="center"><img src="docs/images/logo.svg" height="50px"></p>
2
2
 
3
3
  ## Pay - Payments engine for Ruby on Rails
4
4
 
5
- [![Build Status](https://github.com/pay-rails/pay/workflows/Tests/badge.svg)](https://github.com/pay-rails/pay/actions) [![Gem Version](https://badge.fury.io/rb/pay.svg)](https://badge.fury.io/rb/pay)
5
+ [![Build Status](https://github.com/pay-rails/pay/workflows/Tests/badge.svg)](https://github.com/pay-rails/pay/actions) [![Gem Version](https://badge.fury.io/rb/pay.svg)](https://badge.fury.io/rb/pay)
6
+
7
+ <img src="docs/images/stripe_partner_badge.svg" height="26px">
6
8
 
7
9
  Pay is a payments engine for Ruby on Rails 4.2 and higher.
8
10
 
@@ -11,6 +13,7 @@ Pay is a payments engine for Ruby on Rails 4.2 and higher.
11
13
  - Stripe ([SCA Compatible](https://stripe.com/docs/strong-customer-authentication) using API version `2020-08-27`)
12
14
  - Paddle (SCA Compatible & supports PayPal)
13
15
  - Braintree (supports PayPal)
16
+ - [Fake Processor](docs/fake_processor.md)
14
17
 
15
18
  Want to add a new payment provider? Contributions are welcome and the instructions [are here](https://github.com/jasoncharnes/pay/wiki/New-Payment-Provider).
16
19
 
@@ -36,7 +39,7 @@ gem 'stripe', '< 6.0', '>= 2.8'
36
39
  gem 'braintree', '< 3.0', '>= 2.92.0'
37
40
 
38
41
  # To use Paddle, also include:
39
- gem 'paddle_pay', '~> 0.0.1'
42
+ gem 'paddle_pay', '~> 0.1'
40
43
 
41
44
  # To use Receipts
42
45
  gem 'receipts', '~> 1.0.0'
@@ -150,11 +153,12 @@ development:
150
153
  vendor_id: xxxx
151
154
  vendor_auth_code: yyyy
152
155
  public_key_base64: MII...==
156
+ environment: sandbox
153
157
  ```
154
158
 
155
159
  For Stripe, you can also use the `STRIPE_PUBLIC_KEY`, `STRIPE_PRIVATE_KEY` and `STRIPE_SIGNING_SECRET` environment variables.
156
160
  For Braintree, you can also use `BRAINTREE_MERCHANT_ID`, `BRAINTREE_PUBLIC_KEY`, `BRAINTREE_PRIVATE_KEY`, and `BRAINTREE_ENVIRONMENT` environment variables.
157
- For Paddle, you can also use `PADDLE_VENDOR_ID`, `PADDLE_VENDOR_AUTH_CODE` and `PADDLE_PUBLIC_KEY_BASE64` environment variables.
161
+ For Paddle, you can also use `PADDLE_VENDOR_ID`, `PADDLE_VENDOR_AUTH_CODE`, `PADDLE_PUBLIC_KEY_BASE64` and `PADDLE_ENVIRONMENT` environment variables.
158
162
 
159
163
  ### Generators
160
164
 
@@ -620,6 +624,7 @@ development:
620
624
  vendor_id: xxxx
621
625
  vendor_auth_code: yyyy
622
626
  public_key_base64: MII...==
627
+ environment: sandbox
623
628
  ```
624
629
 
625
630
  Paddle receipts can be retrieved by a charge receipt URL.
@@ -685,6 +690,12 @@ If you'd like to change the views of the payment confirmation page, you can inst
685
690
 
686
691
  [<img src="https://d1jfzjx68gj8xs.cloudfront.net/items/2s3Z0J3Z3b1J1v2K2O1a/Screen%20Shot%202019-10-10%20at%2012.56.32%20PM.png?X-CloudApp-Visitor-Id=51470" alt="Stripe SCA Payment Confirmation" style="zoom: 25%;" />](https://d1jfzjx68gj8xs.cloudfront.net/items/2s3Z0J3Z3b1J1v2K2O1a/Screen%20Shot%202019-10-10%20at%2012.56.32%20PM.png)
687
692
 
693
+ If you use the default views for payment confirmations, and also have a Content Security Policy in place for your application, make sure to add the following domains to their respective configurations in your `content_security_policy.rb` (otherwise these views won't load properly):
694
+
695
+ * `style_src`: `https://unpkg.com`
696
+ * `script_src`: `https://cdn.jsdelivr.net` and `https://js.stripe.com`
697
+ * `frame_src`: `https://js.stripe.com`
698
+
688
699
  #### Background jobs
689
700
 
690
701
  If a user's email is updated and they have a `processor_id` set, Pay will enqueue a background job (EmailSyncJob) to sync the email with the payment processor.
@@ -21,7 +21,7 @@ module Pay
21
21
  store_accessor :data, :paddle_receipt_url
22
22
 
23
23
  # Helpers for payment processors
24
- %w[braintree stripe paddle].each do |processor_name|
24
+ %w[braintree stripe paddle fake_processor].each do |processor_name|
25
25
  define_method "#{processor_name}?" do
26
26
  processor == processor_name
27
27
  end
@@ -50,20 +50,8 @@ module Pay
50
50
  "#{card_type} (**** **** **** #{card_last4})"
51
51
  end
52
52
 
53
- def stripe?
54
- processor == "stripe"
55
- end
56
-
57
- def braintree?
58
- processor == "braintree"
59
- end
60
-
61
53
  def paypal?
62
54
  braintree? && card_type == "PayPal"
63
55
  end
64
-
65
- def paddle?
66
- processor == "paddle"
67
- end
68
56
  end
69
57
  end
@@ -35,7 +35,7 @@ module Pay
35
35
  attribute :prorate, :boolean, default: true
36
36
 
37
37
  # Helpers for payment processors
38
- %w[braintree stripe paddle].each do |processor_name|
38
+ %w[braintree stripe paddle fake_processor].each do |processor_name|
39
39
  define_method "#{processor_name}?" do
40
40
  processor == processor_name
41
41
  end
@@ -13,7 +13,7 @@
13
13
  sessionId: '<%= session.id %>'
14
14
  }).then(function (result) {
15
15
  if (result.error) {
16
- document.getElementById('error-message').innerText = result.error.message;
16
+ document.getElementById("error-for-#{session.id}").innerText = result.error.message;
17
17
  }
18
18
  });
19
19
  });
@@ -27,118 +27,6 @@ en:
27
27
  invalid_payment: "This payment attempt failed because of an invalid payment method."
28
28
  email_required: "Email is required to create a customer"
29
29
  no_processor: "No payment processor selected. Make sure to set the %{class_name}'s `processor` attribute to either 'stripe' or 'braintree'."
30
- stripe:
31
- errors:
32
- account_already_exists: The email address provided for the creation of a deferred account already has an account associated with it. Use the OAuth flow to connect the existing account to your platform.
33
- account_country_invalid_address: The country of the business address provided does not match the country of the account. Businesses must be located in the same country as the account.
34
- account_invalid: The account ID provided as a value for the Stripe-Account header is invalid. Check that your requests are specifying a valid account ID.
35
- account_number_invalid: The bank account number provided is invalid (e.g., missing digits). Bank account information varies from country to country. We recommend creating validations in your entry forms based on the bank account formats we provide.
36
- alipay_upgrade_required: This method for creating Alipay payments is not supported anymore. Please upgrade your integration to use Sources instead.
37
- amount_too_large: The specified amount is greater than the maximum amount allowed. Use a lower amount and try again.
38
- amount_too_small: The specified amount is less than the minimum amount allowed. Use a higher amount and try again.
39
- api_key_expired: The API key provided has expired. Obtain your current API keys from the Dashboard and update your integration to use them.
40
- authentication_required: The payment requires authentication to proceed. If your customer is off session, notify your customer to return to your application and complete the payment. If you provided the error_on_requires_action parameter, then your customer should try another card that does not require authentication.
41
- balance_insufficient: The transfer or payout could not be completed because the associated account does not have a sufficient balance available. Create a new transfer or payout using an amount less than or equal to the account’s available balance.
42
- bank_account_declined: The bank account provided can not be used to charge, either because it is not verified yet or it is not supported.
43
- bank_account_exists: The bank account provided already exists on the specified Customer object. If the bank account should also be attached to a different customer, include the correct customer ID when making the request again.
44
- bank_account_unusable: The bank account provided cannot be used for payouts. A different bank account must be used.
45
- bank_account_unverified: Your Connect platform is attempting to share an unverified bank account with a connected account.
46
- bank_account_verification_failed: The bank account cannot be verified, either because the microdeposit amounts provided do not match the actual amounts, or because verification has failed too many times.
47
- bitcoin_upgrade_required: This method for creating Bitcoin payments is not supported anymore. Please upgrade your integration to use Sources instead.
48
- card_decline_rate_limit_exceeded: This card has been declined too many times. You can try to charge this card again after 24 hours. We suggest reaching out to your customer to make sure they have entered all of their information correctly and that there are no issues with their card.
49
- card_declined: The card has been declined. When a card is declined, the error returned also includes the decline_code attribute with the reason why the card was declined. Refer to our decline codes documentation to learn more.
50
- charge_already_captured: The charge you’re attempting to capture has already been captured. Update the request with an uncaptured charge ID.
51
- charge_already_refunded: The charge you’re attempting to refund has already been refunded. Update the request to use the ID of a charge that has not been refunded.
52
- charge_disputed: The charge you’re attempting to refund has been charged back. Check the disputes documentation to learn how to respond to the dispute.
53
- charge_exceeds_source_limit: This charge would cause you to exceed your rolling-window processing limit for this source type. Please retry the charge later, or contact us to request a higher processing limit.
54
- charge_expired_for_capture: The charge cannot be captured as the authorization has expired. Auth and capture charges must be captured within seven days.
55
- charge_invalid_parameter: One or more provided parameters was not allowed for the given operation on the Charge. Check our API reference or the returned error message to see which values were not correct for that Charge.
56
- clearing_code_unsupported: The clearing code provided is not supported.
57
- country_code_invalid: The country code provided was invalid.
58
- country_unsupported: Your platform attempted to create a custom account in a country that is not yet supported. Make sure that users can only sign up in countries supported by custom accounts.
59
- coupon_expired: The coupon provided for a subscription or order has expired. Either create a new coupon, or use an existing one that is valid.
60
- customer_max_payment_methods: The maximum number of PaymentMethods for this Customer has been reached. Either detach some PaymentMethods from this Customer or proceed with a different Customer.
61
- customer_max_subscriptions: The maximum number of subscriptions for a customer has been reached. Contact us if you are receiving this error.
62
- email_invalid: The email address is invalid (e.g., not properly formatted). Check that the email address is properly formatted and only includes allowed characters.
63
- expired_card: The card has expired. Check the expiration date or use a different card.
64
- idempotency_key_in_use: The idempotency key provided is currently being used in another request. This occurs if your integration is making duplicate requests simultaneously.
65
- incorrect_address: The card’s address is incorrect. Check the card’s address or use a different card.
66
- incorrect_cvc: The card’s security code is incorrect. Check the card’s security code or use a different card.
67
- incorrect_number: The card number is incorrect. Check the card’s number or use a different card.
68
- incorrect_zip: The card’s postal code is incorrect. Check the card’s postal code or use a different card.
69
- instant_payouts_unsupported: This card is not eligible for Instant Payouts. Try a debit card from a supported bank.
70
- intent_invalid_state: Intent is not the state that is rquired to perform the operation.
71
- intent_verification_method_missing: Intent does not have verification method specified in its PaymentMethodOptions object.
72
- invalid_card_type: The card provided as an external account is not supported for payouts. Provide a non-prepaid debit card instead.
73
- invalid_characters: This value provided to the field contains characters that are unsupported by the field.
74
- invalid_charge_amount: The specified amount is invalid. The charge amount must be a positive integer in the smallest currency unit, and not exceed the minimum or maximum amount.
75
- invalid_cvc: The card’s security code is invalid. Check the card’s security code or use a different card.
76
- invalid_expiry_month: The card’s expiration month is incorrect. Check the expiration date or use a different card.
77
- invalid_expiry_year: The card’s expiration year is incorrect. Check the expiration date or use a different card.
78
- invalid_number: The card number is invalid. Check the card details or use a different card.
79
- invalid_source_usage: The source cannot be used because it is not in the correct state (e.g., a charge request is trying to use a source with a pending, failed, or consumed source). Check the status of the source you are attempting to use.
80
- invoice_no_customer_line_items: An invoice cannot be generated for the specified customer as there are no pending invoice items. Check that the correct customer is being specified or create any necessary invoice items first.
81
- invoice_no_payment_method_types: An invoice cannot be finalized because there are no payment method types available to process the payment. Your invoice template settings or the invoice’s payment_settings might be restricting which payment methods are available, or you might need to activate more payment methods in the Dashboard.
82
- invoice_no_subscription_line_items: An invoice cannot be generated for the specified subscription as there are no pending invoice items. Check that the correct subscription is being specified or create any necessary invoice items first.
83
- invoice_not_editable: The specified invoice can no longer be edited. Instead, consider creating additional invoice items that will be applied to the next invoice. You can either manually generate the next invoice or wait for it to be automatically generated at the end of the billing cycle.
84
- invoice_payment_intent_requires_action: This payment requires additional user action before it can be completed successfully. Payment can be completed using the PaymentIntent associated with the invoice. See this page for more details.
85
- invoice_upcoming_none: There is no upcoming invoice on the specified customer to preview. Only customers with active subscriptions or pending invoice items have invoices that can be previewed.
86
- livemode_mismatch: Test and live mode API keys, requests, and objects are only available within the mode they are in.
87
- lock_timeout: This object cannot be accessed right now because another API request or Stripe process is currently accessing it. If you see this error intermittently, retry the request. If you see this error frequently and are making multiple concurrent requests to a single object, make your requests serially or at a lower rate. See the rate limit documentation for more details.
88
- missing: Both a customer and source ID have been provided, but the source has not been saved to the customer. To create a charge for a customer with a specified source, you must first save the card details.
89
- not_allowed_on_standard_account: Transfers and payouts on behalf of a Standard connected account are not allowed.
90
- order_creation_failed: The order could not be created. Check the order details and then try again.
91
- order_required_settings: The order could not be processed as it is missing required information. Check the information provided and try again.
92
- order_status_invalid: The order cannot be updated because the status provided is either invalid or does not follow the order lifecycle (e.g., an order cannot transition from created to fulfilled without first transitioning to paid).
93
- order_upstream_timeout: The request timed out. Try again later.
94
- out_of_inventory: The SKU is out of stock. If more stock is available, update the SKU’s inventory quantity and try again.
95
- parameter_invalid_empty: One or more required values were not provided. Make sure requests include all required parameters.
96
- parameter_invalid_integer: One or more of the parameters requires an integer, but the values provided were a different type. Make sure that only supported values are provided for each attribute. Refer to our API documentation to look up the type of data each attribute supports.
97
- parameter_invalid_string_blank: One or more values provided only included whitespace. Check the values in your request and update any that contain only whitespace.
98
- parameter_invalid_string_empty: One or more required string values is empty. Make sure that string values contain at least one character.
99
- parameter_missing: One or more required values are missing. Check our API documentation to see which values are required to create or modify the specified resource.
100
- parameter_unknown: The request contains one or more unexpected parameters. Remove these and try again.
101
- parameters_exclusive: Two or more mutually exclusive parameters were provided. Check our API documentation or the returned error message to see which values are permitted when creating or modifying the specified resource.
102
- payment_intent_action_required: The provided payment method requires customer actions to complete, but error_on_requires_action was set. If you’d like to add this payment method to your integration, we recommend that you first upgrade your integration to handle actions.
103
- payment_intent_authentication_failure: The provided payment method has failed authentication. Provide a new payment method to attempt to fulfill this PaymentIntent again.
104
- payment_intent_incompatible_payment_method: The PaymentIntent expected a payment method with different properties than what was provided.
105
- payment_intent_invalid_parameter: One or more provided parameters was not allowed for the given operation on the PaymentIntent. Check our API reference or the returned error message to see which values were not correct for that PaymentIntent.
106
- payment_intent_payment_attempt_failed: The latest payment attempt for the PaymentIntent has failed. Check the last_payment_error property on the PaymentIntent for more details, and provide a new payment method to attempt to fulfill this PaymentIntent again.
107
- payment_intent_unexpected_state: The PaymentIntent’s state was incompatible with the operation you were trying to perform.
108
- payment_method_invalid_parameter: Invalid parameter was provided in the payment method object. Check our API documentation or the returned error message for more context.
109
- payment_method_provider_decline: The payment was declined by the issuer or customer. Check the last_payment_error property on the PaymentIntent for more details, and provide a new payment method to attempt to fulfill this PaymentIntent again.
110
- payment_method_provider_timeout: The payment method failed due to a timeout. Check the last_payment_error property on the PaymentIntent for more details, and provide a new payment method to attempt to fulfill this PaymentIntent again.
111
- payment_method_unactivated: The operation cannot be performed as the payment method used has not been activated. Activate the payment method in the Dashboard, then try again.
112
- payment_method_unexpected_state: The provided payment method’s state was incompatible with the operation you were trying to perform. Confirm that the payment method is in an allowed state for the given operation before attempting to perform it.
113
- payment_method_unsupported_type: The API only supports payment methods of certain types.
114
- payouts_not_allowed: Payouts have been disabled on the connected account. Check the connected account’s status to see if any additional information needs to be provided, or if payouts have been disabled for another reason.
115
- platform_api_key_expired: The API key provided by your Connect platform has expired. This occurs if your platform has either generated a new key or the connected account has been disconnected from the platform. Obtain your current API keys from the Dashboard and update your integration, or reach out to the user and reconnect the account.
116
- postal_code_invalid: The postal code provided was incorrect.
117
- processing_error: An error occurred while processing the card. Try again later or with a different payment method.
118
- product_inactive: The product this SKU belongs to is no longer available for purchase.
119
- rate_limit: Too many requests hit the API too quickly. We recommend an exponential backoff of your requests.
120
- resource_already_exists: A resource with a user-specified ID (e.g., plan or coupon) already exists. Use a different, unique value for id and try again.
121
- resource_missing: The ID provided is not valid. Either the resource does not exist, or an ID for a different resource has been provided.
122
- routing_number_invalid: The bank routing number provided is invalid.
123
- secret_key_required: The API key provided is a publishable key, but a secret key is required. Obtain your current API keys from the Dashboard and update your integration to use them.
124
- sepa_unsupported_account: Your account does not support SEPA payments.
125
- setup_attempt_failed: The latest setup attempt for the SetupIntent has failed. Check the last_setup_error property on the SetupIntent for more details, and provide a new payment method to attempt to set it up again.
126
- setup_intent_authentication_failure: The provided payment method has failed authentication. Provide a new payment method to attempt to fulfill this SetupIntent again.
127
- setup_intent_invalid_parameter: One or more provided parameters was not allowed for the given operation on the SetupIntent. Check our API reference or the returned error message to see which values were not correct for that SetupIntent.
128
- setup_intent_unexpected_state: The SetupIntent’s state was incompatible with the operation you were trying to perform.
129
- shipping_calculation_failed: Shipping calculation failed as the information provided was either incorrect or could not be verified.
130
- sku_inactive: The SKU is inactive and no longer available for purchase. Use a different SKU, or make the current SKU active again.
131
- state_unsupported: Occurs when providing the legal_entity information for a U.S. custom account, if the provided state is not supported. (This is mostly associated states and territories.)
132
- tax_id_invalid: The tax ID number provided is invalid (e.g., missing digits). Tax ID information varies from country to country, but must be at least nine digits.
133
- taxes_calculation_failed: Tax calculation for the order failed.
134
- terminal_location_country_unsupported: Terminal is currently only available in some countries. Locations in your country cannot be created in livemode.
135
- testmode_charges_only: Your account has not been activated and can only make test charges. Activate your account in the Dashboard to begin processing live charges.
136
- tls_version_unsupported: Your integration is using an older version of TLS that is unsupported. You must be using TLS 1.2 or above.
137
- token_already_used: The token provided has already been used. You must create a new token before you can retry this request.
138
- token_in_use: The token provided is currently being used in another request. This occurs if your integration is making duplicate requests simultaneously.
139
- transfers_not_allowed: The requested transfer cannot be created. Contact us if you are receiving this error.
140
- upstream_order_creation_failed: The order could not be created. Check the order details and then try again.
141
- url_invalid: The URL provided is invalid.
142
30
  braintree:
143
31
  authorization: "Either the data you submitted is malformed and does not match the API or the API key you used may not be authorized to perform this action."
144
32
 
@@ -151,4 +39,4 @@ en:
151
39
  subscription_renewing:
152
40
  subject: "Your upcoming subscription renewal"
153
41
  payment_action_required:
154
- subject: "Confirm your payment"
42
+ subject: "Confirm your payment"
data/lib/pay.rb CHANGED
@@ -10,6 +10,7 @@ module Pay
10
10
 
11
11
  # Payment processors
12
12
  autoload :Braintree, "pay/braintree"
13
+ autoload :FakeProcessor, "pay/fake_processor"
13
14
  autoload :Paddle, "pay/paddle"
14
15
  autoload :Stripe, "pay/stripe"
15
16
 
data/lib/pay/billable.rb CHANGED
@@ -24,6 +24,9 @@ module Pay
24
24
  attribute :plan, :string
25
25
  attribute :quantity, :integer
26
26
  attribute :card_token, :string
27
+ attribute :pay_fake_processor_allowed, :boolean, default: false
28
+
29
+ validate :pay_fake_processor_is_allowed, if: :processor_changed?
27
30
  end
28
31
 
29
32
  def payment_processor
@@ -31,6 +34,7 @@ module Pay
31
34
  end
32
35
 
33
36
  def payment_processor_for(name)
37
+ raise Error, "No payment processor set. Assign a payment processor with 'object.processor = :stripe' or any supported processor." if name.blank?
34
38
  "Pay::#{name.to_s.classify}::Billable".constantize
35
39
  end
36
40
 
@@ -155,5 +159,10 @@ module Pay
155
159
  # Generic trials don't have plans or custom names
156
160
  plan.nil? && name == "default" && on_generic_trial?
157
161
  end
162
+
163
+ def pay_fake_processor_is_allowed
164
+ return unless processor == "fake_processor"
165
+ errors.add(:processor, "must be a valid payment processor") unless pay_fake_processor_allowed?
166
+ end
158
167
  end
159
168
  end
@@ -37,8 +37,8 @@ module Pay
37
37
 
38
38
  result.customer
39
39
  end
40
- rescue ::Braintree::AuthorizationError
41
- raise Pay::Braintree::AuthorizationError
40
+ rescue ::Braintree::AuthorizationError => e
41
+ raise Pay::Braintree::AuthorizationError, e
42
42
  rescue ::Braintree::BraintreeError => e
43
43
  raise Pay::Braintree::Error, e
44
44
  end
@@ -57,8 +57,8 @@ module Pay
57
57
  raise Pay::Braintree::Error, result unless result.success?
58
58
 
59
59
  save_transaction(result.transaction)
60
- rescue ::Braintree::AuthorizationError
61
- raise Pay::Braintree::AuthorizationError
60
+ rescue ::Braintree::AuthorizationError => e
61
+ raise Pay::Braintree::AuthorizationError, e
62
62
  rescue ::Braintree::BraintreeError => e
63
63
  raise Pay::Braintree::Error, e
64
64
  end
@@ -84,8 +84,8 @@ module Pay
84
84
  raise Pay::Braintree::Error, result unless result.success?
85
85
 
86
86
  billable.create_pay_subscription(result.subscription, "braintree", name, plan, status: :active)
87
- rescue ::Braintree::AuthorizationError
88
- raise Pay::Braintree::AuthorizationError
87
+ rescue ::Braintree::AuthorizationError => e
88
+ raise Pay::Braintree::AuthorizationError, e
89
89
  rescue ::Braintree::BraintreeError => e
90
90
  raise Pay::Braintree::Error, e
91
91
  end
@@ -94,6 +94,8 @@ module Pay
94
94
  #
95
95
  # Returns true if successful
96
96
  def update_card(token)
97
+ customer unless processor_id?
98
+
97
99
  result = gateway.payment_method.create(
98
100
  customer_id: processor_id,
99
101
  payment_method_nonce: token,
@@ -107,8 +109,8 @@ module Pay
107
109
  update_card_on_file result.payment_method
108
110
  update_subscriptions_to_payment_method(result.payment_method.token)
109
111
  true
110
- rescue ::Braintree::AuthorizationError
111
- raise Pay::Braintree::AuthorizationError
112
+ rescue ::Braintree::AuthorizationError => e
113
+ raise Pay::Braintree::AuthorizationError, e
112
114
  rescue ::Braintree::BraintreeError => e
113
115
  raise Pay::Braintree::Error, e
114
116
  end
@@ -17,7 +17,6 @@ module Pay
17
17
 
18
18
  def refund!(amount_to_refund)
19
19
  Pay.braintree_gateway.transaction.refund(processor_id, amount_to_refund / 100.0)
20
-
21
20
  pay_charge.update(amount_refunded: amount_to_refund)
22
21
  rescue ::Braintree::BraintreeError => e
23
22
  raise Pay::Braintree::Error, e
@@ -1,8 +1,22 @@
1
1
  module Pay
2
2
  module Braintree
3
3
  class Error < Pay::Error
4
- def message
5
- result.message
4
+ # For any manually raised Braintree error results (for failure responses)
5
+ # we can raise this exception manually but treat it as if we wrapped an exception
6
+
7
+ attr_reader :result
8
+
9
+ def initialize(result)
10
+ if result.is_a?(::Braintree::ErrorResult)
11
+ super(result.message)
12
+ @result = result
13
+ else
14
+ super
15
+ end
16
+ end
17
+
18
+ def cause
19
+ super || result
6
20
  end
7
21
  end
8
22
  end
@@ -9,13 +9,13 @@ module Pay
9
9
  :name,
10
10
  :on_trial?,
11
11
  :owner,
12
- :processor_subscription,
13
12
  :processor_id,
13
+ :processor_plan,
14
+ :processor_subscription,
14
15
  :prorate,
15
16
  :prorate?,
16
- :processor_plan,
17
- :quantity?,
18
17
  :quantity,
18
+ :quantity?,
19
19
  :trial_ends_at,
20
20
  to: :pay_subscription
21
21
 
data/lib/pay/errors.rb CHANGED
@@ -1,10 +1,6 @@
1
1
  module Pay
2
+ # https://avdi.codes/exception-causes-in-ruby-2-1/
2
3
  class Error < StandardError
3
- attr_reader :result
4
-
5
- def initialize(result = nil)
6
- @result = result
7
- end
8
4
  end
9
5
 
10
6
  class PaymentError < StandardError
@@ -0,0 +1,8 @@
1
+ module Pay
2
+ module FakeProcessor
3
+ autoload :Billable, "pay/fake_processor/billable"
4
+ autoload :Charge, "pay/fake_processor/charge"
5
+ autoload :Subscription, "pay/fake_processor/subscription"
6
+ autoload :Error, "pay/fake_processor/error"
7
+ end
8
+ end
@@ -0,0 +1,60 @@
1
+ module Pay
2
+ module FakeProcessor
3
+ class Billable
4
+ attr_reader :billable
5
+
6
+ delegate :processor_id,
7
+ :processor_id?,
8
+ :email,
9
+ :customer_name,
10
+ :card_token,
11
+ to: :billable
12
+
13
+ def initialize(billable)
14
+ @billable = billable
15
+ end
16
+
17
+ def customer
18
+ billable
19
+ end
20
+
21
+ def charge(amount, options = {})
22
+ billable.charges.create(
23
+ processor: :fake_processor,
24
+ processor_id: rand(100_000_000),
25
+ amount: amount,
26
+ card_type: :fake,
27
+ card_last4: 1234,
28
+ card_exp_month: Date.today.month,
29
+ card_exp_year: Date.today.year
30
+ )
31
+ end
32
+
33
+ def subscribe(name: Pay.default_product_name, plan: Pay.default_plan_name, **options)
34
+ subscription = OpenStruct.new id: rand(1_000_000)
35
+ billable.create_pay_subscription(subscription, :fake_processor, name, plan, status: :active, quantity: options.fetch(:quantity, 1))
36
+ end
37
+
38
+ def update_card(payment_method_id)
39
+ billable.update(
40
+ card_type: :fake,
41
+ card_last4: 1234,
42
+ card_exp_month: Date.today.month,
43
+ card_exp_year: Date.today.year
44
+ )
45
+ end
46
+
47
+ def update_email!
48
+ # pass
49
+ end
50
+
51
+ def processor_subscription(subscription_id, options = {})
52
+ billable.subscriptions.find_by(processor: :fake_processor, processor_id: subscription_id)
53
+ end
54
+
55
+ def trial_end_date(subscription)
56
+ Date.today
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,21 @@
1
+ module Pay
2
+ module FakeProcessor
3
+ class Charge
4
+ attr_reader :pay_charge
5
+
6
+ delegate :processor_id, :owner, to: :pay_charge
7
+
8
+ def initialize(pay_charge)
9
+ @pay_charge = pay_charge
10
+ end
11
+
12
+ def charge
13
+ pay_charge
14
+ end
15
+
16
+ def refund!(amount_to_refund)
17
+ pay_charge.update(amount_refunded: amount_to_refund)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,6 @@
1
+ module Pay
2
+ module FakeProcessor
3
+ class Error < Pay::Error
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,55 @@
1
+ module Pay
2
+ module FakeProcessor
3
+ class Subscription
4
+ attr_reader :pay_subscription
5
+
6
+ delegate :canceled?,
7
+ :ends_at,
8
+ :on_trial?,
9
+ :owner,
10
+ :processor_subscription,
11
+ :processor_id,
12
+ :prorate,
13
+ :processor_plan,
14
+ :quantity?,
15
+ :quantity,
16
+ to: :pay_subscription
17
+
18
+ def initialize(pay_subscription)
19
+ @pay_subscription = pay_subscription
20
+ end
21
+
22
+ def cancel
23
+ pay_subscription.update(ends_at: Time.current.end_of_month)
24
+ end
25
+
26
+ def cancel_now!
27
+ pay_subscription.update(ends_at: Time.current, status: :canceled)
28
+ end
29
+
30
+ def on_grace_period?
31
+ canceled? && Time.zone.now < ends_at
32
+ end
33
+
34
+ def paused?
35
+ false
36
+ end
37
+
38
+ def pause
39
+ raise NotImplementedError, "FakeProcessor does not support pausing subscriptions"
40
+ end
41
+
42
+ def resume
43
+ unless on_grace_period?
44
+ raise StandardError, "You can only resume subscriptions within their grace period."
45
+ end
46
+
47
+ pay_subscription.update(ends_at: nil, status: :active)
48
+ end
49
+
50
+ def swap(plan)
51
+ pay_subscription.update(processor_plan: plan)
52
+ end
53
+ end
54
+ end
55
+ end
data/lib/pay/paddle.rb CHANGED
@@ -19,6 +19,7 @@ module Pay
19
19
  def self.setup
20
20
  ::PaddlePay.config.vendor_id = vendor_id
21
21
  ::PaddlePay.config.vendor_auth_code = vendor_auth_code
22
+ ::PaddlePay.config.environment = environment
22
23
 
23
24
  configure_webhooks
24
25
  end
@@ -31,6 +32,10 @@ module Pay
31
32
  find_value_by_name(:paddle, :vendor_auth_code)
32
33
  end
33
34
 
35
+ def self.environment
36
+ find_value_by_name(:paddle, :environment) || "production"
37
+ end
38
+
34
39
  def self.public_key_base64
35
40
  find_value_by_name(:paddle, :public_key_base64)
36
41
  end
@@ -39,6 +44,13 @@ module Pay
39
44
  options.merge(owner_sgid: owner.to_sgid.to_s).to_json
40
45
  end
41
46
 
47
+ def self.owner_from_passthrough(passthrough)
48
+ passthrough_json = JSON.parse(passthrough)
49
+ GlobalID::Locator.locate_signed(passthrough_json["owner_sgid"])
50
+ rescue JSON::ParserError
51
+ nil
52
+ end
53
+
42
54
  def self.configure_webhooks
43
55
  Pay::Webhooks.configure do |events|
44
56
  events.subscribe "paddle.subscription_created", Pay::Paddle::Webhooks::SubscriptionCreated.new
@@ -1,9 +1,7 @@
1
1
  module Pay
2
2
  module Paddle
3
3
  class Error < Pay::Error
4
- def message
5
- I18n.t("errors.paddle.#{result.code}", default: result.message)
6
- end
4
+ delegate :message, to: :cause
7
5
  end
8
6
  end
9
7
  end
@@ -3,8 +3,10 @@ module Pay
3
3
  class Subscription
4
4
  attr_reader :pay_subscription
5
5
 
6
- delegate :canceled?,
6
+ delegate :active?,
7
+ :canceled?,
7
8
  :ends_at,
9
+ :name,
8
10
  :on_trial?,
9
11
  :owner,
10
12
  :paddle_paused_from,
@@ -12,8 +14,10 @@ module Pay
12
14
  :processor_plan,
13
15
  :processor_subscription,
14
16
  :prorate,
17
+ :prorate?,
15
18
  :quantity,
16
19
  :quantity?,
20
+ :trial_ends_at,
17
21
  to: :pay_subscription
18
22
 
19
23
  def initialize(pay_subscription)
@@ -13,7 +13,7 @@ module Pay
13
13
  owner = Pay.find_billable(processor: :paddle, processor_id: event["user_id"])
14
14
 
15
15
  if owner.nil?
16
- owner = owner_by_passtrough(event["passthrough"], event["subscription_plan_id"])
16
+ owner = Pay::Paddle.owner_from_passthrough(event["passthrough"])
17
17
  owner&.update!(processor: "paddle", processor_id: event["user_id"])
18
18
  end
19
19
 
@@ -44,15 +44,6 @@ module Pay
44
44
 
45
45
  subscription.save!
46
46
  end
47
-
48
- private
49
-
50
- def owner_by_passtrough(passthrough, product_id)
51
- passthrough_json = JSON.parse(passthrough)
52
- GlobalID::Locator.locate_signed(passthrough_json["owner_sgid"])
53
- rescue JSON::ParserError
54
- nil
55
- end
56
47
  end
57
48
  end
58
49
  end
@@ -4,7 +4,17 @@ module Pay
4
4
  class SubscriptionPaymentSucceeded
5
5
  def call(event)
6
6
  billable = Pay.find_billable(processor: :paddle, processor_id: event["user_id"])
7
- return unless billable.present?
7
+
8
+ if billable.nil?
9
+ billable = Pay::Paddle.owner_from_passthrough(event["passthrough"])
10
+ billable&.update!(processor: "paddle", processor_id: event["user_id"])
11
+ end
12
+
13
+ if billable.nil?
14
+ Rails.logger.error("[Pay] Unable to find Pay::Billable with owner: '#{event["passthrough"]}'. Searched these models: #{Pay.billable_models.join(", ")}")
15
+ return
16
+ end
17
+
8
18
  return if billable.charges.where(processor_id: event["subscription_payment_id"]).any?
9
19
 
10
20
  charge = create_charge(billable, event)
data/lib/pay/stripe.rb CHANGED
@@ -11,6 +11,7 @@ module Pay
11
11
  autoload :CustomerDeleted, "pay/stripe/webhooks/customer_deleted"
12
12
  autoload :CustomerUpdated, "pay/stripe/webhooks/customer_updated"
13
13
  autoload :PaymentActionRequired, "pay/stripe/webhooks/payment_action_required"
14
+ autoload :PaymentIntentSucceeded, "pay/stripe/webhooks/payment_intent_succeeded"
14
15
  autoload :PaymentMethodUpdated, "pay/stripe/webhooks/payment_method_updated"
15
16
  autoload :SubscriptionCreated, "pay/stripe/webhooks/subscription_created"
16
17
  autoload :SubscriptionDeleted, "pay/stripe/webhooks/subscription_deleted"
@@ -50,6 +51,8 @@ module Pay
50
51
  events.subscribe "stripe.charge.succeeded", Pay::Stripe::Webhooks::ChargeSucceeded.new
51
52
  events.subscribe "stripe.charge.refunded", Pay::Stripe::Webhooks::ChargeRefunded.new
52
53
 
54
+ events.subscribe "stripe.payment_intent.succeeded", Pay::Stripe::Webhooks::PaymentIntentSucceeded.new
55
+
53
56
  # Warn user of upcoming charges for their subscription. This is handy for
54
57
  # notifying annual users their subscription will renew shortly.
55
58
  # This probably should be ignored for monthly subscriptions.
@@ -1,9 +1,7 @@
1
1
  module Pay
2
2
  module Stripe
3
3
  class Error < Pay::Error
4
- def message
5
- I18n.t("errors.stripe.#{result.code}", default: result.message)
6
- end
4
+ delegate :message, to: :cause
7
5
  end
8
6
  end
9
7
  end
@@ -3,16 +3,20 @@ module Pay
3
3
  class Subscription
4
4
  attr_reader :pay_subscription
5
5
 
6
- delegate :canceled?,
6
+ delegate :active?,
7
+ :canceled?,
7
8
  :ends_at,
9
+ :name,
8
10
  :on_trial?,
9
11
  :owner,
10
- :processor_subscription,
11
12
  :processor_id,
12
- :prorate,
13
13
  :processor_plan,
14
- :quantity?,
14
+ :processor_subscription,
15
+ :prorate,
16
+ :prorate?,
15
17
  :quantity,
18
+ :quantity?,
19
+ :trial_ends_at,
16
20
  to: :pay_subscription
17
21
 
18
22
  def initialize(pay_subscription)
@@ -0,0 +1,27 @@
1
+ module Pay
2
+ module Stripe
3
+ module Webhooks
4
+ class PaymentIntentSucceeded
5
+ def call(event)
6
+ object = event.data.object
7
+ billable = Pay.find_billable(processor: :stripe, processor_id: object.customer)
8
+
9
+ return unless billable.present?
10
+
11
+ object.charges.data.each do |charge|
12
+ next if billable.charges.where(processor_id: charge.id).any?
13
+
14
+ charge = Pay::Stripe::Billable.new(billable).save_pay_charge(charge)
15
+ notify_user(billable, charge)
16
+ end
17
+ end
18
+
19
+ def notify_user(billable, charge)
20
+ if Pay.send_emails && charge.respond_to?(:receipt)
21
+ Pay::UserMailer.with(billable: billable, charge: charge).receipt.deliver_later
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
data/lib/pay/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Pay
2
- VERSION = "2.6.3"
2
+ VERSION = "2.6.8"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pay
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.3
4
+ version: 2.6.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Charnes
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-02-23 00:00:00.000000000 Z
12
+ date: 2021-04-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -166,6 +166,11 @@ files:
166
166
  - lib/pay/engine.rb
167
167
  - lib/pay/env.rb
168
168
  - lib/pay/errors.rb
169
+ - lib/pay/fake_processor.rb
170
+ - lib/pay/fake_processor/billable.rb
171
+ - lib/pay/fake_processor/charge.rb
172
+ - lib/pay/fake_processor/error.rb
173
+ - lib/pay/fake_processor/subscription.rb
169
174
  - lib/pay/paddle.rb
170
175
  - lib/pay/paddle/billable.rb
171
176
  - lib/pay/paddle/charge.rb
@@ -189,6 +194,7 @@ files:
189
194
  - lib/pay/stripe/webhooks/customer_deleted.rb
190
195
  - lib/pay/stripe/webhooks/customer_updated.rb
191
196
  - lib/pay/stripe/webhooks/payment_action_required.rb
197
+ - lib/pay/stripe/webhooks/payment_intent_succeeded.rb
192
198
  - lib/pay/stripe/webhooks/payment_method_updated.rb
193
199
  - lib/pay/stripe/webhooks/subscription_created.rb
194
200
  - lib/pay/stripe/webhooks/subscription_deleted.rb