lightrail_stripe 1.0.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
+ SHA1:
3
+ metadata.gz: '0870e939ca6c8301e58b197d60406d893ebcc04a'
4
+ data.tar.gz: 48214fff42bd21c8a8067c38e198b219a7ec73bc
5
+ SHA512:
6
+ metadata.gz: 97aede42f3020b5cd3ba135b96f8848c510f6a36965d459490686260a11869323f8031da5112d80e1e8eeb0d238171bf859b4504ec9574b408b4c44c00554069
7
+ data.tar.gz: 6249dbb30a3e91c2f13eff5efdede46be2aa258722523e8e1bc467cececce57141c936d4bcc933b9a9c3c799dfa86b4049c20d7f2edd3fa2a4881de304797cd0
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
13
+
14
+ # environment variables
15
+ .env
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.0.0
5
+ before_install: gem install bundler -v 1.15.1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in lightrail_stripe.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Giftbit
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,277 @@
1
+ # Lightrail Stripe Integration Gem
2
+
3
+ Lightrail is a modern platform for digital account credits, gift cards, promotions, and points (to learn more, visit [Lightrail](https://www.lightrail.com/)). The Lightrail Stripe gem provides a client library for developers to easily use Lightrail alongside [Stripe](https://stripe.com/).
4
+
5
+ If you are looking for specific use cases or other languages, check out [related projects](https://github.com/Giftbit/lightrail-stripe-ruby#related-projects). For a complete list of all Lightrail libraries and integrations, check out the [Lightrail Integration page](https://github.com/Giftbit/Lightrail-API-Docs/blob/usecases/Integrations.md).
6
+
7
+ ## Features
8
+
9
+ - Simple order checkout which supports Lightrail gift card redemption alongside a Stripe payment
10
+ - Convenient wrappers for making common calls to the Lightrail API using a similar interface to Stripe
11
+
12
+ This gem depends on the [Lightrail Client Gem](https://github.com/Giftbit/lightrail-client-ruby) for communicating with the Lightrail API. It offers convenient wrappers for common API calls for those who are already using [Stripe's Ruby gem](https://github.com/stripe/stripe-ruby) and are familiar with that interface: `Lightrail::LightrailCharge.create`, `Lightrail::Refund.create`, etc. These calls will return class instance objects instead of hashes, with convenience methods and properties that can be accessed with dot notation (for a more detailed example, see the [balance check](#balance-check) code in the following section):
13
+
14
+ ```ruby
15
+ gift_value = Lightrail::LightrailValue.retrieve_by_card_id(<CARD ID>)
16
+ gift_value.maximum_value #=> 3500
17
+ ```
18
+
19
+ ## Related Projects
20
+
21
+ - [Lightrail Client Gem](https://github.com/Giftbit/lightrail-client-ruby)
22
+ - [Lightrail-Stripe Java Integration](https://github.com/Giftbit/lightrail-stripe-java)
23
+ - [Lightrail Java Client](https://github.com/Giftbit/lightrail-client-java)
24
+
25
+ ## Usage
26
+
27
+ Before using any parts of the library, you need to set up your Lightrail API key, and if you have not already done so, you will also need to set up your Stripe API key:
28
+
29
+ ```ruby
30
+ Lightrail.api_key = '<your lightrail API key>'
31
+ Stripe.api_key = '<your stripe API key>'
32
+ ```
33
+
34
+ *A note on sample code snippets: for reasons of legibility, the output for most calls has been simplified. Attributes of response objects that are not relevant here have been omitted.*
35
+
36
+ ### Order Checkout Using `StripeLightrailSplitTenderCharge`
37
+
38
+ `StripeLightrailSplitTenderCharge` is a class designed to resemble the interface of a Stripe `Charge` class which transparently splits the transaction between Lightrail and Stripe. The Lightrail parameter could be one of the following:
39
+
40
+ - `code`, specifying a gift card by its code, or
41
+ - `cardId`, specifying a gift card by its card ID
42
+ - `contactId`, specifying a customer by their contact ID (generated by Lightrail)
43
+ - `shopperId`, specifying a customer by their shopper ID (generated by your ecommerce system)
44
+
45
+ The Stripe parameter could be:
46
+
47
+ - `source`, indicating a Stripe token, or
48
+ - `customer`, indicating a Stripe customer ID
49
+
50
+ You must also pass in the amount to charge to Lightrail.
51
+
52
+ Here is a simple example:
53
+
54
+ ```ruby
55
+ split_tender_charge_params = {
56
+ amount: 1000,
57
+ currency: 'USD',
58
+ code: '<GIFT CODE>',
59
+ source: '<STRIPE TOKEN>',
60
+ }
61
+ lightrail_amount = 450
62
+
63
+ split_tender_charge = Lightrail::StripeLightrailSplitTenderCharge.create(split_tender_charge_params, lightrail_amount)
64
+ ```
65
+
66
+ If you don't provide any Lightrail payment parameters and set the `lightrail_amount` to 0, the entire transaction will be charged to Stripe. Similarly, if you don't provide any Stripe payment parameters and set the `lightrail_amount` to the full amount, the library will attempt to charge the entire transaction to Lightrail. If the value of the gift card is not enough to cover the entire transaction amount and no Stripe payment method is included, you will receive a `BadParameterError` asking you to provide a Stripe parameter.
67
+
68
+ There is also a wrapper method that can determine the Stripe/Lightrail split automatically: `Lightrail::StripeLightrailSplitTenderCharge.create_with_automatic_split`. This method accepts the same `charge_params` as the default `.create`, but does not expect a `stripe_amount` or `lightrail_amount`. When both a Lightrail and a Stripe payment method are provided to this method, the library will try to split the payment in such a way that Lightrail contributes to the payment as much as possible. This usually means:
69
+
70
+ - If the Lightrail value is sufficient, the entire transaction will be charged to the gift card.
71
+ - If the transaction amount is larger than the Lightrail value, the remainder will be charged to Stripe.
72
+
73
+ ## Managing Lightrail Gift Cards and Accounts
74
+
75
+ A Lightrail Gift Card is a virtual device for issuing gift values. Each gift card has a specific `currency`, a `cardId`, and a `fullCode`, which is a unique unguessable alphanumeric string, usually released to the gift recipient in confidence. The recipient of the gift card can present the `fullCode` to redeem the gift value. For further explanation of cards and codes see the [Lightrail API documentation](https://www.lightrail.com/docs/).
76
+
77
+ Customer Accounts are values attached to a customer and are commonly used for customer rewards and account credit programs. For further explanation of this concept check out the [Lightrail API documentation](https://www.lightrail.com/docs/).
78
+
79
+ ### Balance Check
80
+
81
+ Use the `Lightrail::LightrailValue` class to check the balance of gift cards or customer accounts.
82
+
83
+ For **gift cards**, use `.retrieve_by_card` or `.retrieve_by_code`:
84
+
85
+ ```ruby
86
+ gift_balance_details = Lightrail::LightrailValue.retrieve_card_details("<GIFT CARD ID>")
87
+ # or use the fullCode:
88
+ # gift_balance_details = Lightrail::LightrailValue.retrieve_code_details("<GIFT CODE>")
89
+
90
+ #=> <Lightrail::LightrailValue:0x007fe24b16f500
91
+ @principal=
92
+ {
93
+ 'currentValue' => 3000,
94
+ 'state' => 'ACTIVE',
95
+ 'expires' => nil,
96
+ 'startDate' => nil,
97
+ 'programId' => 'program-123456',
98
+ 'valueStoreId' => 'value-123456'},
99
+ @attached=[{'currentValue' => 500,
100
+ 'state' => 'ACTIVE',
101
+ #...},
102
+ {'currentValue' => 250,
103
+ 'state' => 'EXPIRED',
104
+ #...}],
105
+ @currency="USD",
106
+ @cardType="GIFT_CARD",
107
+ @balanceDate="2017-05-29T13:37:02.756Z",
108
+ @cardId="card-123456>
109
+
110
+ gift_maximum_value = gift_balance_details.maximum_value
111
+ #=> 3500
112
+ ```
113
+
114
+ For **accounts**, use `.retrieve_by_contact_id` or `.retrieve_by_shopper_id`. Note that because a single contact can hold account value in several different currencies, it is also necessary to specify the account currency to perform a balance check:
115
+
116
+ ```ruby
117
+ account_balance_details = Lightrail::Value.retrieve_by_contact_id({
118
+ contact_id: '<CONTACT ID>',
119
+ currency: 'USD'
120
+ })
121
+
122
+ # or use a customer's shopper_id:
123
+ # account_balance_details = Lightrail::Value.retrieve_by_shopper_id({
124
+ # shopper_id: '<CONTACT ID>',
125
+ # currency: 'USD'
126
+ # })
127
+
128
+ #=> <Lightrail::LightrailValue:0x007fe24b16f500
129
+ @principal = {'currentValue' => 3000, ...}>
130
+ ```
131
+
132
+ ### Charging a Gift Card or Customer Account
133
+
134
+ Use`Lightrail::LightrailCharge.create` to charge a gift card or a customer account. The minimum required parameters are either the gift card's `fullCode` or `cardId` OR the customer's `contactId` or `shopperId`, the `currency`, and the `amount` of the transaction (a positive integer in the smallest currency unit, e.g., 500 cents is 5 USD):
135
+
136
+ ```ruby
137
+ gift_charge = Lightrail::LightrailCharge.create({
138
+ amount: 1850,
139
+ currency: 'USD',
140
+ code: '<GIFT CODE>' # or card_id/contact_id/shopper_id
141
+ })
142
+ #=> <Lightrail::LightrailCharge:0x007fdb62206e68
143
+ @value=-1850,
144
+ @userSuppliedId="17223eff",
145
+ @transactionType="DRAWDOWN",
146
+ @currency="USD",
147
+ @transactionId="transaction-cd245",
148
+ #...>
149
+ ```
150
+
151
+ **A note on idempotency:** All calls to create or act on transactions (refund, void, capture) can optionally take a `userSuppliedId` parameter. The `userSuppliedId` is a client-side identifier (unique string) which is used to ensure idempotency (for more details, see the [API documentation](https://www.lightrail.com/docs/)). If you do not provide a `userSuppliedId`, the gem will create one for you for any calls that require one.
152
+
153
+ ```ruby
154
+ gift_charge = Lightrail::LightrailCharge.create({
155
+ amount: 1850,
156
+ currency: 'USD',
157
+ code: '<GIFT CODE>',
158
+ userSuppliedId: 'order-13jg9s0era9023-u9a-0ea'
159
+ })
160
+ ```
161
+
162
+ Note that Lightrail does not support currency exchange and the currency provided in this method must match the currency of the gift card or the customer's account.
163
+
164
+ For more details on the parameters that you can pass in for a charge request and the response that you will get back, see the [Lightrail API documentation](https://www.lightrail.com/docs/).
165
+
166
+ ### Authorize-Capture Flow
167
+
168
+ You can create a pending charge for a Lightrail gift card or customer account the same way you would with Stripe for a credit card — simply by adding `capture: false` to your charge parameters. The pending charge object returned from this method call will also have convenience methods to directly `#capture!` or `#cancel!` that charge:
169
+
170
+ ```ruby
171
+ gift_charge = Lightrail::LightrailCharge.create({
172
+ amount: 1850,
173
+ currency: 'USD',
174
+ code: '<GIFT CODE>', # or card_id/contact_id/shopper_id
175
+ capture: false,
176
+ })
177
+ # later on
178
+ gift_charge.capture!
179
+ #=> <Lightrail::LightrailCharge:0x007fdb633531d0
180
+ @value=-1850,
181
+ @userSuppliedId="17223eff",
182
+ @dateCreated="2017-05-29T13:37:02.756Z",
183
+ @transactionType="DRAWDOWN",
184
+ @transactionAccessMethod="RAWCODE",
185
+ @cardId="<GIFT CARD ID>",
186
+ @currency="USD",
187
+ @transactionId="transaction-cd245",
188
+ @parentTransactionId="transaction-b9d4444">
189
+
190
+ # or
191
+ gift_charge.cancel!
192
+ #=> <Lightrail::LightrailCharge:0x007fdb62854018
193
+ @value=-1850,
194
+ @userSuppliedId="17223eff",
195
+ @dateCreated="2017-05-29T13:37:02.756Z",
196
+ @transactionType="PENDING_VOID",
197
+ @transactionAccessMethod="RAWCODE",
198
+ @cardId="<GIFT CARD ID>",
199
+ @currency="USD",
200
+ @transactionId="transaction-0ce6f05",
201
+ @parentTransactionId="transaction-b9d4444">
202
+ ```
203
+
204
+ Note that `#capture!` and `#cancel!` will each return a **new transaction** and will not modify the instance they are called on. These new transactions will have their own `transactionId`. If you need to record the transaction ID of the captured or canceled charge, you can get it from the object returned by these methods (a new instance of `LightrailCharge`).
205
+
206
+ ### Refunding a Charge
207
+
208
+ You can undo a charge by using the `Lightrail::Refund` class and passing in the transaction instance you wish to refund. This will create a new `refund` transaction which will return the charged amount back to the card or account. The return object will be an instance of `Lightrail::Refund`. If you need the transaction ID of the refund transaction, you can find it on this object.
209
+
210
+ ```ruby
211
+ gift_charge = Lightrail::LightrailCharge.create(<CHARGE PARAMS>)
212
+
213
+ # later on
214
+ Lightrail::Refund.create(gift_charge)
215
+ #=> <Lightrail::Refund:0x007fdb62854018
216
+ @value=1850,
217
+ @userSuppliedId="873b08ab",
218
+ @dateCreated="2017-05-29T13:37:02.756Z",
219
+ @transactionType="DRAWDOWN_REFUND",
220
+ @transactionAccessMethod="CARDID",
221
+ @cardId="<GIFT CARD ID>",
222
+ @currency="USD",
223
+ @transactionId="transaction-0f2a67",
224
+ @parentTransactionId="transaction-2271e3">
225
+ ```
226
+
227
+ Note that this does not necessarily mean that the refunded amount is available for a re-charge. In the edge case where the funds for the original charge came from a promotion which has now expired, refunding will return those funds back to the now-expired value store and therefore the value will not be available for re-charge. To learn more about using value stores for temporary promotions, see the [Lightrail API docs](https://github.com/Giftbit/Lightrail-API-Docs/blob/master/use-cases/promotions.md).
228
+
229
+ ### Funding a Gift Card or Customer Account
230
+
231
+ To add funds to a card or account, you can use the `Lightrail::LightrailFund` class. Note that the Lightrail API does not permit funding a gift card by its `code` and the only way to fund a card is by providing its `cardId`. Customer accounts can be funded by supplying either their `contact_id` or their `shopper_id` along with the `currency` of the account.
232
+
233
+ ```ruby
234
+ gift_fund = Lightrail::LightrailFund.create({
235
+ amount: 500,
236
+ currency: 'USD',
237
+ card_id: '<GIFT CARD ID>' # or contact_id/shopper_id
238
+ })
239
+ #=> <Lightrail::LightrailFund:0x007fb7b18b96b8
240
+ @value=500,
241
+ @userSuppliedId="4f86a1be",
242
+ @dateCreated="2017-05-29T13:37:02.756Z",
243
+ @transactionType="FUND",
244
+ @transactionAccessMethod="CARDID",
245
+ @cardId="<GIFT CARD ID>",
246
+ @currency="USD",
247
+ @transactionId="transaction-240eca6">
248
+ ```
249
+
250
+
251
+ ## Installation
252
+
253
+ This gem is available on RubyGems.org. To use it in your project, add this line to your application's Gemfile:
254
+
255
+ ```ruby
256
+ gem 'lightrail_stripe'
257
+ ```
258
+
259
+ And then execute:
260
+
261
+ ```
262
+ $ bundle
263
+ ```
264
+
265
+ ## Contributing
266
+
267
+ Bug reports and pull requests are welcome on GitHub at https://github.com/Giftbit/lightrail-stripe-ruby.
268
+
269
+ ## Development
270
+
271
+ After checking out the repo, run `bin/setup` to install dependencies, then run `bundle exec rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
272
+
273
+ To install this gem onto your local machine, run `bundle exec rake install`.
274
+
275
+ ## License
276
+
277
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/_config.yml ADDED
@@ -0,0 +1 @@
1
+ theme: jekyll-theme-slate
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "lightrail_stripe"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,28 @@
1
+ module Lightrail
2
+ class SplitTenderValidator < Lightrail::Validator
3
+
4
+ def self.validate_split_tender_charge_params! (split_tender_charge_params)
5
+ begin
6
+ return true if ((split_tender_charge_params.is_a? Hash) &&
7
+ Lightrail::Validator.validate_amount!(split_tender_charge_params[:amount]) &&
8
+ Lightrail::Validator.validate_currency!(split_tender_charge_params[:currency]) &&
9
+ (self.has_lightrail_payment_option?(split_tender_charge_params) ||
10
+ self.has_stripe_payment_option?(split_tender_charge_params)))
11
+ rescue Lightrail::LightrailArgumentError
12
+ end
13
+ raise Lightrail::LightrailArgumentError.new("Invalid split_tender_charge_params: #{split_tender_charge_params.inspect}")
14
+ end
15
+
16
+ def self.has_stripe_payment_option?(charge_params)
17
+ charge_params.has_key?(:source) || charge_params.has_key?(:customer)
18
+ end
19
+
20
+ def self.has_lightrail_payment_option?(charge_params)
21
+ Lightrail::Validator.has_valid_code?(charge_params) ||
22
+ Lightrail::Validator.has_valid_card_id?(charge_params) ||
23
+ Lightrail::Validator.has_valid_contact_id?(charge_params) ||
24
+ Lightrail::Validator.has_valid_shopper_id?(charge_params)
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,133 @@
1
+ module Lightrail
2
+ class StripeLightrailSplitTenderCharge < Lightrail::LightrailObject
3
+ attr_accessor :lightrail_charge, :stripe_charge, :payment_summary
4
+
5
+ def self.create (charge_params, lr_share)
6
+ # Convert to Translator.translate_split_tender_charge_params!()
7
+ Lightrail::SplitTenderValidator.validate_split_tender_charge_params!(charge_params)
8
+
9
+ total_amount = charge_params[:amount]
10
+ currency = charge_params[:currency]
11
+
12
+ stripe_share = total_amount - lr_share
13
+
14
+ if lr_share > 0 # start with lightrail charge first
15
+ lightrail_charge_params = Lightrail::Translator.construct_lightrail_pending_charge_params_from_split_tender(charge_params, lr_share)
16
+
17
+ lightrail_pending_transaction = Lightrail::LightrailCharge.create(lightrail_charge_params)
18
+
19
+ if stripe_share > 0 # continue to stripe charge
20
+ begin
21
+ stripe_params = Lightrail::Translator.charge_params_split_tender_to_stripe(charge_params, stripe_share)
22
+ stripe_transaction = Stripe::Charge.create(stripe_params)
23
+ lightrail_metadata = Lightrail::Translator.construct_lightrail_metadata_for_split_tender_charge(stripe_transaction)
24
+ rescue
25
+ lightrail_pending_transaction.cancel!
26
+ raise $!, "Stripe payment failed: #{$!}", $!.backtrace
27
+ end
28
+ end
29
+
30
+ lightrail_captured_transaction = lightrail_pending_transaction.capture!(lightrail_metadata)
31
+
32
+ else # all to stripe
33
+ stripe_params = Lightrail::Translator.charge_params_split_tender_to_stripe(charge_params, stripe_share)
34
+ stripe_transaction = Stripe::Charge.create(stripe_params)
35
+
36
+ end
37
+
38
+
39
+ split_tender_charge_payment_summary = {
40
+ total_amount: total_amount,
41
+ currency: currency,
42
+ lightrail_amount: lightrail_captured_transaction ? lightrail_captured_transaction.value : 0,
43
+ stripe_amount: stripe_transaction ? stripe_transaction.amount : 0,
44
+ }
45
+
46
+ self.new({lightrail_charge: lightrail_captured_transaction, stripe_charge: stripe_transaction, payment_summary: split_tender_charge_payment_summary})
47
+ end
48
+
49
+ def self.create_with_automatic_split (charge_params)
50
+ Lightrail::SplitTenderValidator.validate_split_tender_charge_params!(charge_params)
51
+
52
+ split_amounts = self.determine_split!(charge_params)
53
+ lr_share = split_amounts[:lightrail_amount]
54
+ self.create(charge_params, lr_share)
55
+ end
56
+
57
+ def self.simulate (charge_params, lr_share)
58
+ Lightrail::SplitTenderValidator.validate_split_tender_charge_params!(charge_params)
59
+
60
+ total_amount = charge_params[:amount]
61
+ currency = charge_params[:currency]
62
+
63
+ stripe_share = total_amount - lr_share
64
+
65
+ if lr_share > 0 # only need to simulate Lightrail transaction
66
+ lightrail_charge_params = Lightrail::Translator.construct_lightrail_charge_params_from_split_tender(charge_params, lr_share)
67
+
68
+ lightrail_simulated_transaction = Lightrail::LightrailCharge.simulate(lightrail_charge_params)
69
+
70
+ lr_final_share = lightrail_simulated_transaction.value
71
+ end
72
+
73
+ split_tender_charge_payment_summary = {
74
+ total_amount: total_amount,
75
+ currency: currency,
76
+ lightrail_amount: lr_final_share ? lr_final_share : 0,
77
+ stripe_amount: lr_final_share ? total_amount - lr_final_share.abs : total_amount
78
+ }
79
+
80
+ self.new({lightrail_charge: lightrail_simulated_transaction, stripe_charge: nil, payment_summary: split_tender_charge_payment_summary})
81
+ end
82
+
83
+ def self.simulate_with_automatic_split (charge_params)
84
+ Lightrail::SplitTenderValidator.validate_split_tender_charge_params!(charge_params)
85
+
86
+ split_amounts = self.determine_split!(charge_params)
87
+ lr_share = split_amounts[:lightrail_amount]
88
+ self.simulate(charge_params, lr_share)
89
+ end
90
+
91
+
92
+ private
93
+
94
+ def self.determine_split!(charge_params)
95
+ total_amount = charge_params[:amount]
96
+ contact_id = Lightrail::Contact.get_contact_id_from_id_or_shopper_id(charge_params)
97
+ code = Lightrail::Validator.get_code(charge_params)
98
+ card_id = Lightrail::Validator.get_card_id(charge_params)
99
+
100
+ lr_share = if contact_id || code || card_id
101
+ charge_params_for_simulate = charge_params.clone
102
+ charge_params_for_simulate[:value] = -charge_params[:amount] || -charge_params['amount']
103
+
104
+ if contact_id
105
+ Lightrail::Account.simulate_charge(charge_params_for_simulate)['value'].abs
106
+ elsif code
107
+ Lightrail::Code.simulate_charge(charge_params_for_simulate)['value'].abs
108
+ elsif card_id
109
+ Lightrail::Card.simulate_charge(charge_params_for_simulate)['value'].abs
110
+ end
111
+
112
+ else
113
+ nil
114
+ end
115
+
116
+ if lr_share && (lr_share < total_amount) && (Lightrail::SplitTenderValidator.has_stripe_payment_option?(charge_params))
117
+ stripe_share = total_amount - lr_share
118
+ lr_share = stripe_share < 50 ? lr_share - (50-stripe_share) : lr_share
119
+ stripe_share = total_amount - lr_share
120
+ elsif lr_share && (lr_share < total_amount)
121
+ raise Lightrail::BadParameterError.new('Please provide a Stripe payment method to complete the transaction.')
122
+ else
123
+ stripe_share = charge_params[:amount]
124
+ end
125
+
126
+ {
127
+ lightrail_amount: lr_share || 0,
128
+ stripe_amount: stripe_share
129
+ }
130
+ end
131
+
132
+ end
133
+ end
@@ -0,0 +1,86 @@
1
+ module Lightrail
2
+ class Translator
3
+
4
+ def self.charge_params_stripe_to_lightrail(stripe_style_params)
5
+ self.stripe_params_to_lightrail!(stripe_style_params, true)
6
+ end
7
+
8
+ def self.charge_instance_to_hash!(charge_instance)
9
+ if charge_instance.is_a? Lightrail::LightrailCharge
10
+ charge_hash = {}
11
+ charge_instance.instance_variables.each {|instance_variable| charge_hash[instance_variable.to_s.delete('@')] = charge_instance.instance_variable_get(instance_variable)}
12
+ charge_hash
13
+ else
14
+ raise Lightrail::LightrailArgumentError.new("Translator.charge_instance_to_hash! received #{charge_instance.inspect}")
15
+ end
16
+ end
17
+
18
+ def self.fund_params_stripe_to_lightrail(stripe_style_params)
19
+ self.stripe_params_to_lightrail!(stripe_style_params, false)
20
+ end
21
+
22
+ def self.construct_lightrail_charge_params_from_split_tender(split_tender_charge_params, lr_share)
23
+ lightrail_params = split_tender_charge_params.clone
24
+
25
+ lightrail_params[:pending] ||= lightrail_params[:capture] === nil ? false : !lightrail_params.delete(:capture)
26
+ lightrail_params[:value] = -lr_share
27
+ lightrail_params.delete(:amount)
28
+
29
+ lightrail_params[:contact_id] ||= Lightrail::Validator.get_contact_id(lightrail_params)
30
+ lightrail_params[:shopper_id] ||= Lightrail::Validator.get_shopper_id(lightrail_params)
31
+ if (lightrail_params[:contact_id] || lightrail_params[:shopper_id])
32
+ lightrail_params = Lightrail::Account.replace_contact_id_or_shopper_id_with_card_id(lightrail_params)
33
+ end
34
+
35
+ lightrail_params[:code] ||= Lightrail::Validator.get_code(lightrail_params)
36
+ lightrail_params[:cardId] ||= Lightrail::Validator.get_card_id(lightrail_params)
37
+
38
+ lightrail_params[:userSuppliedId] ||= Lightrail::Validator.get_or_create_user_supplied_id(lightrail_params)
39
+
40
+ lightrail_params
41
+ end
42
+
43
+ def self.construct_lightrail_pending_charge_params_from_split_tender(split_tender_charge_params, lr_share)
44
+ lightrail_params = self.construct_lightrail_charge_params_from_split_tender(split_tender_charge_params, lr_share)
45
+ lightrail_params[:pending] = true
46
+
47
+ lightrail_params
48
+ end
49
+
50
+ def self.charge_params_split_tender_to_stripe(split_tender_charge_params, stripe_share)
51
+ stripe_params = split_tender_charge_params.clone
52
+ stripe_params[:amount] = stripe_share
53
+
54
+ Lightrail::Constants::LIGHTRAIL_PAYMENT_METHODS.each {|charge_param_key| stripe_params.delete(charge_param_key)}
55
+ Lightrail::Constants::LIGHTRAIL_USER_SUPPLIED_ID_KEYS.each {|supplied_id_key| stripe_params.delete(supplied_id_key)}
56
+
57
+ stripe_params
58
+ end
59
+
60
+ def self.construct_lightrail_metadata_for_split_tender_charge(stripe_transaction)
61
+ {
62
+ metadata: {
63
+ splitTenderChargeDetails: {
64
+ stripeTransactionId: stripe_transaction.id
65
+ }
66
+ }
67
+ }
68
+ end
69
+
70
+ private
71
+
72
+ def self.stripe_params_to_lightrail!(transaction_params, convert_amount_to_negative_value)
73
+ lr_transaction_params = transaction_params.clone
74
+ lr_transaction_params[:pending] ||= lr_transaction_params[:capture] === nil ? false : !lr_transaction_params.delete(:capture)
75
+ lr_transaction_params[:userSuppliedId] ||= Lightrail::Validator.get_or_create_user_supplied_id(lr_transaction_params)
76
+ lr_transaction_params[:value] ||= convert_amount_to_negative_value ? -(lr_transaction_params[:amount].abs) : lr_transaction_params[:amount].abs
77
+
78
+ if (Lightrail::Validator.has_valid_contact_id?(lr_transaction_params) || Lightrail::Validator.has_valid_shopper_id?(lr_transaction_params))
79
+ lr_transaction_params = Lightrail::Account.replace_contact_id_or_shopper_id_with_card_id(lr_transaction_params)
80
+ end
81
+
82
+ lr_transaction_params
83
+ end
84
+
85
+ end
86
+ end
@@ -0,0 +1,3 @@
1
+ module LightrailStripe
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,59 @@
1
+ module Lightrail
2
+ class LightrailCharge < Lightrail::LightrailObject
3
+ attr_accessor :transactionId, :value, :userSuppliedId, :dateCreated, :transactionType, :transactionAccessMethod, :giftbitUserId, :cardId, :currency, :codeLastFour, :metadata
4
+
5
+ def self.create (charge_params)
6
+ Lightrail::Validator.validate_charge_object! (charge_params)
7
+ charge_params_to_send_to_lightrail = Lightrail::Translator.charge_params_stripe_to_lightrail(charge_params)
8
+
9
+ charge_method = charge_params_to_send_to_lightrail[:code] ? 'code' : 'cardId'
10
+
11
+ response = (charge_method == 'code') ?
12
+ Lightrail::Code.charge(charge_params_to_send_to_lightrail) :
13
+ Lightrail::Card.charge(charge_params_to_send_to_lightrail)
14
+
15
+ self.new(response)
16
+ end
17
+
18
+ def self.simulate (charge_params)
19
+ Lightrail::Validator.validate_charge_object! (charge_params)
20
+ charge_params_to_send_to_lightrail = Lightrail::Translator.charge_params_stripe_to_lightrail(charge_params)
21
+
22
+ charge_method = charge_params_to_send_to_lightrail[:code] ? 'code' : 'cardId'
23
+
24
+ response = (charge_method == 'code') ?
25
+ Lightrail::Code.simulate_charge(charge_params_to_send_to_lightrail) :
26
+ Lightrail::Card.simulate_charge(charge_params_to_send_to_lightrail)
27
+
28
+ self.new(response)
29
+ end
30
+
31
+ def cancel! (new_request_body=nil)
32
+ handle_pending(self, 'void', new_request_body)
33
+ end
34
+
35
+ def capture! (new_request_body=nil)
36
+ handle_pending(self, 'capture', new_request_body)
37
+ end
38
+
39
+ private
40
+
41
+ def handle_pending (original_transaction_response, void_or_capture, new_request_body=nil)
42
+
43
+ hash_of_original_transaction_response = original_transaction_response.clone
44
+ hash_of_original_transaction_response = Lightrail::Translator.charge_instance_to_hash!(hash_of_original_transaction_response)
45
+
46
+ Lightrail::Validator.validate_transaction_response!(hash_of_original_transaction_response)
47
+
48
+ transaction_id = Lightrail::Validator.get_transaction_id(hash_of_original_transaction_response)
49
+
50
+ body = new_request_body || {}
51
+ body[:userSuppliedId] ||= Lightrail::Validator.get_or_create_user_supplied_id_with_action_suffix(body, transaction_id, void_or_capture)
52
+
53
+ response = Lightrail::Transaction.handle_transaction(hash_of_original_transaction_response, void_or_capture, body)
54
+
55
+ Lightrail::LightrailCharge.new(response)
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,15 @@
1
+ module Lightrail
2
+ class LightrailFund < Lightrail::LightrailObject
3
+ attr_accessor :transactionId, :value, :userSuppliedId, :dateCreated, :transactionType, :transactionAccessMethod, :giftbitUserId, :cardId, :currency, :codeLastFour
4
+
5
+ def self.create(fund_object)
6
+ Lightrail::Validator.validate_fund_object!(fund_object)
7
+
8
+ fund_object_to_send_to_lightrail = Lightrail::Translator.fund_params_stripe_to_lightrail(fund_object)
9
+
10
+ response = Lightrail::Card.fund(fund_object_to_send_to_lightrail)
11
+
12
+ self.new(response)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,43 @@
1
+ module Lightrail
2
+ class LightrailValue < Lightrail::LightrailObject
3
+ attr_accessor :valueStores, :currency, :cardType, :asAtDate, :cardId
4
+
5
+ def self.retrieve_code_details (code)
6
+ Lightrail::Validator.validate_code! (code)
7
+ response = Lightrail::Code.get_details(code)
8
+ self.new(response)
9
+ end
10
+
11
+ def self.retrieve_card_details (card_id)
12
+ Lightrail::Validator.validate_card_id!(card_id)
13
+ response = Lightrail::Card.get_details(card_id)
14
+ self.new(response)
15
+ end
16
+
17
+ def self.retrieve_contact_account_details (contact_id, currency)
18
+ Lightrail::Validator.validate_contact_id!(contact_id)
19
+ Lightrail::Validator.validate_currency!(currency)
20
+ response = Lightrail::Account.get_account_details({contact_id: contact_id, currency: currency})
21
+ self.new(response)
22
+ end
23
+
24
+ def self.retrieve_by_shopper_id (shopper_id, currency)
25
+ Lightrail::Validator.validate_shopper_id!(shopper_id)
26
+ Lightrail::Validator.validate_currency!(currency)
27
+ response = Lightrail::Account.get_account_balance_details({shopper_id: shopper_id, currency: currency})
28
+ self.new(response)
29
+ end
30
+
31
+
32
+ def maximum_value
33
+ maximum_value = 0
34
+ self.valueStores.each do |valueStore|
35
+ if valueStore['state'] == 'ACTIVE'
36
+ maximum_value += valueStore['value']
37
+ end
38
+ end
39
+ maximum_value
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,22 @@
1
+ module Lightrail
2
+ class Refund < Lightrail::LightrailObject
3
+ attr_accessor :transactionId, :value, :userSuppliedId, :dateCreated, :transactionType, :transactionAccessMethod, :giftbitUserId, :cardId, :currency, :parentTransactionId, :metadata, :codeLastFour
4
+
5
+ def self.create(original_transaction_response, new_request_body = {})
6
+ hash_of_original_transaction_response = original_transaction_response.clone
7
+ hash_of_original_transaction_response = Lightrail::Translator.charge_instance_to_hash!(hash_of_original_transaction_response)
8
+
9
+ Lightrail::Validator.validate_transaction_response! (hash_of_original_transaction_response)
10
+
11
+ transaction_id = Lightrail::Validator.get_transaction_id(hash_of_original_transaction_response)
12
+
13
+ body = new_request_body
14
+ body[:userSuppliedId] ||= Lightrail::Validator.get_or_create_user_supplied_id_with_action_suffix(body, transaction_id, 'refund')
15
+
16
+ response = Lightrail::Transaction.handle_transaction(hash_of_original_transaction_response, :refund, body)
17
+
18
+ self.new(response)
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,26 @@
1
+ require "faraday"
2
+ require "openssl"
3
+ require "json"
4
+ require "securerandom"
5
+
6
+ require "lightrail_client"
7
+ require "stripe"
8
+
9
+ require "lightrail_stripe/version"
10
+
11
+ require "lightrail_stripe/translator"
12
+ require "lightrail_stripe/split_tender_validator"
13
+
14
+ require "lightrail_stripe/wrappers/lightrail_charge"
15
+ require "lightrail_stripe/wrappers/lightrail_fund"
16
+ require "lightrail_stripe/wrappers/lightrail_value"
17
+ require "lightrail_stripe/wrappers/refund"
18
+
19
+ require "lightrail_stripe/stripe_lightrail_split_tender_charge"
20
+
21
+ module Lightrail
22
+ class << self
23
+ attr_accessor :api_base, :api_key
24
+ end
25
+ @api_base = 'https://api.lightrail.com/v1'
26
+ end
@@ -0,0 +1,45 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "lightrail_stripe/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "lightrail_stripe"
8
+ spec.version = LightrailStripe::VERSION
9
+ spec.authors = ["Lightrail"]
10
+ spec.email = ["tana.j@lightrail.com"]
11
+
12
+ spec.summary = "A integration gem to use Stripe and Lightrail for payment processing"
13
+ spec.description = "Acquire and retain customers using account credits, gift cards, promotions, and points."
14
+ spec.homepage = "https://www.lightrail.com/"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
21
+ else
22
+ raise "RubyGems 2.0 or newer is required to protect against " \
23
+ "public gem pushes."
24
+ end
25
+
26
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
+ f.match(%r{^(test|spec|features)/})
28
+ end
29
+ spec.bindir = "exe"
30
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ["lib"]
32
+
33
+ spec.required_ruby_version = '~> 2.3'
34
+
35
+ spec.add_development_dependency "bundler", "~> 1.15"
36
+ spec.add_development_dependency "rake", "~> 10.0"
37
+ spec.add_development_dependency "rspec", "~> 3.0"
38
+ spec.add_development_dependency "pry-byebug", "~>3.4"
39
+
40
+ spec.add_runtime_dependency "lightrail_client", "~>1.0"
41
+ spec.add_runtime_dependency "stripe", "~>3.0"
42
+ spec.add_runtime_dependency "faraday", "~>0.12"
43
+ spec.add_runtime_dependency "json", "~>1.7"
44
+ spec.add_runtime_dependency "openssl", "~>2.0"
45
+ end
metadata ADDED
@@ -0,0 +1,193 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lightrail_stripe
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Lightrail
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-01-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.15'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.15'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry-byebug
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.4'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.4'
69
+ - !ruby/object:Gem::Dependency
70
+ name: lightrail_client
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: stripe
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: faraday
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.12'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.12'
111
+ - !ruby/object:Gem::Dependency
112
+ name: json
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.7'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.7'
125
+ - !ruby/object:Gem::Dependency
126
+ name: openssl
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '2.0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '2.0'
139
+ description: Acquire and retain customers using account credits, gift cards, promotions,
140
+ and points.
141
+ email:
142
+ - tana.j@lightrail.com
143
+ executables: []
144
+ extensions: []
145
+ extra_rdoc_files: []
146
+ files:
147
+ - ".gitignore"
148
+ - ".rspec"
149
+ - ".travis.yml"
150
+ - Gemfile
151
+ - LICENSE.txt
152
+ - README.md
153
+ - Rakefile
154
+ - _config.yml
155
+ - bin/console
156
+ - bin/setup
157
+ - lib/lightrail_stripe.rb
158
+ - lib/lightrail_stripe/split_tender_validator.rb
159
+ - lib/lightrail_stripe/stripe_lightrail_split_tender_charge.rb
160
+ - lib/lightrail_stripe/translator.rb
161
+ - lib/lightrail_stripe/version.rb
162
+ - lib/lightrail_stripe/wrappers/lightrail_charge.rb
163
+ - lib/lightrail_stripe/wrappers/lightrail_fund.rb
164
+ - lib/lightrail_stripe/wrappers/lightrail_value.rb
165
+ - lib/lightrail_stripe/wrappers/refund.rb
166
+ - lightrail_stripe.gemspec
167
+ homepage: https://www.lightrail.com/
168
+ licenses:
169
+ - MIT
170
+ metadata:
171
+ allowed_push_host: https://rubygems.org
172
+ post_install_message:
173
+ rdoc_options: []
174
+ require_paths:
175
+ - lib
176
+ required_ruby_version: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '2.3'
181
+ required_rubygems_version: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: '0'
186
+ requirements: []
187
+ rubyforge_project:
188
+ rubygems_version: 2.6.12
189
+ signing_key:
190
+ specification_version: 4
191
+ summary: A integration gem to use Stripe and Lightrail for payment processing
192
+ test_files: []
193
+ has_rdoc: