solidus_paypal_braintree 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +26 -0
- data/README.md +196 -0
- data/Rakefile +30 -0
- data/app/assets/javascripts/spree/backend/solidus_paypal_braintree.js +66 -0
- data/app/assets/javascripts/spree/braintree_hosted_form.js +98 -0
- data/app/assets/javascripts/spree/checkout/braintree.js +60 -0
- data/app/assets/javascripts/spree/frontend/paypal_button.js +182 -0
- data/app/assets/javascripts/spree/frontend/solidus_paypal_braintree.js +188 -0
- data/app/assets/stylesheets/spree/backend/solidus_paypal_braintree.scss +28 -0
- data/app/assets/stylesheets/spree/frontend/solidus_paypal_braintree.css +16 -0
- data/app/controllers/solidus_paypal_braintree/client_tokens_controller.rb +21 -0
- data/app/helpers/braintree_admin_helper.rb +18 -0
- data/app/models/application_record.rb +3 -0
- data/app/models/solidus_paypal_braintree/configuration.rb +5 -0
- data/app/models/solidus_paypal_braintree/customer.rb +4 -0
- data/app/models/solidus_paypal_braintree/gateway.rb +323 -0
- data/app/models/solidus_paypal_braintree/response.rb +52 -0
- data/app/models/solidus_paypal_braintree/source.rb +73 -0
- data/app/models/solidus_paypal_braintree/transaction.rb +30 -0
- data/app/models/solidus_paypal_braintree/transaction_address.rb +66 -0
- data/app/models/solidus_paypal_braintree/transaction_import.rb +92 -0
- data/app/models/spree/store_decorator.rb +11 -0
- data/app/overrides/admin_navigation_menu.rb +6 -0
- data/app/views/spree/shared/_braintree_hosted_fields.html.erb +26 -0
- data/config/initializers/braintree.rb +1 -0
- data/config/locales/en.yml +30 -0
- data/config/routes.rb +12 -0
- data/db/migrate/20160830061749_create_solidus_paypal_braintree_sources.rb +16 -0
- data/db/migrate/20160906201711_create_solidus_paypal_braintree_customers.rb +11 -0
- data/db/migrate/20161114231422_create_solidus_paypal_braintree_configurations.rb +11 -0
- data/db/migrate/20161125172005_add_braintree_configuration_to_stores.rb +9 -0
- data/db/migrate/20170203191030_add_credit_card_to_braintree_configuration.rb +6 -0
- data/db/migrate/20170505193712_add_null_constraint_to_sources.rb +30 -0
- data/db/migrate/20170508085402_add_not_null_constraint_to_sources_payment_type.rb +11 -0
- data/lib/controllers/backend/solidus_paypal_braintree/configurations_controller.rb +30 -0
- data/lib/controllers/frontend/solidus_paypal_braintree/checkouts_controller.rb +27 -0
- data/lib/controllers/frontend/solidus_paypal_braintree/transactions_controller.rb +61 -0
- data/lib/generators/solidus_paypal_braintree/install/install_generator.rb +37 -0
- data/lib/solidus_paypal_braintree.rb +10 -0
- data/lib/solidus_paypal_braintree/country_mapper.rb +35 -0
- data/lib/solidus_paypal_braintree/engine.rb +53 -0
- data/lib/solidus_paypal_braintree/factories.rb +18 -0
- data/lib/solidus_paypal_braintree/version.rb +3 -0
- data/lib/views/backend/solidus_paypal_braintree/configurations/_admin_tab.html.erb +3 -0
- data/lib/views/backend/solidus_paypal_braintree/configurations/list.html.erb +30 -0
- data/lib/views/backend/spree/admin/payments/source_forms/_paypal_braintree.html.erb +16 -0
- data/lib/views/backend/spree/admin/payments/source_views/_paypal_braintree.html.erb +34 -0
- data/lib/views/backend_v1.2/spree/admin/payments/source_forms/_paypal_braintree.html.erb +16 -0
- data/lib/views/frontend/spree/checkout/payment/_paypal_braintree.html.erb +52 -0
- metadata +350 -0
@@ -0,0 +1,182 @@
|
|
1
|
+
/**
|
2
|
+
* Constructor for PayPal button object
|
3
|
+
* @constructor
|
4
|
+
* @param {object} element - The DOM element of your PayPal button
|
5
|
+
*/
|
6
|
+
function PaypalButton(element) {
|
7
|
+
this.element = element;
|
8
|
+
}
|
9
|
+
|
10
|
+
/**
|
11
|
+
* Creates the PayPal session using the provided options and enables the button
|
12
|
+
*
|
13
|
+
* @param {object} options - The options passed to tokenize when constructing
|
14
|
+
* the PayPal instance
|
15
|
+
*
|
16
|
+
* See {@link https://braintree.github.io/braintree-web/3.9.0/PayPal.html#tokenize}
|
17
|
+
*/
|
18
|
+
PaypalButton.prototype.initialize = function(options) {
|
19
|
+
var self = this;
|
20
|
+
|
21
|
+
/* This sets the payment method id returned by fetchToken on the PaypalButton
|
22
|
+
* instance so that we can use it to build the transaction params later. */
|
23
|
+
SolidusPaypalBraintree.fetchToken(function(token, paymentMethodId) {
|
24
|
+
self.paymentMethodId = paymentMethodId;
|
25
|
+
|
26
|
+
SolidusPaypalBraintree.initializeWithDataCollector(token, function(client) {
|
27
|
+
self.createPaypalInstance(client, function(paypal) {
|
28
|
+
|
29
|
+
self.initializePaypalSession({
|
30
|
+
paypalInstance: paypal,
|
31
|
+
paypalButton: self.element,
|
32
|
+
paypalOptions: options
|
33
|
+
}, self.tokenizeCallback.bind(self));
|
34
|
+
});
|
35
|
+
|
36
|
+
});
|
37
|
+
});
|
38
|
+
};
|
39
|
+
|
40
|
+
PaypalButton.prototype.createPaypalInstance = function(braintreeClient, readyCallback) {
|
41
|
+
braintree.paypal.create({
|
42
|
+
client: braintreeClient
|
43
|
+
}, function (paypalErr, paypalInstance) {
|
44
|
+
if (paypalErr) {
|
45
|
+
console.error("Error creating PayPal:", paypalErr);
|
46
|
+
return;
|
47
|
+
}
|
48
|
+
readyCallback(paypalInstance);
|
49
|
+
});
|
50
|
+
};
|
51
|
+
|
52
|
+
/* Initializes and begins the Paypal session
|
53
|
+
*
|
54
|
+
* @param config Configuration settings for the session
|
55
|
+
* @param config.paypalInstance {object} The Paypal instance returned by Braintree
|
56
|
+
* @param config.paypalButton {object} The button DOM element
|
57
|
+
* @param config.paypalOptions {object} Configuration options for Paypal
|
58
|
+
* @param config.error {tokenizeErrorCallback} Callback function for tokenize errors
|
59
|
+
* @param {tokenizeCallback} callback Callback function for tokenization
|
60
|
+
*/
|
61
|
+
PaypalButton.prototype.initializePaypalSession = function(config, callback) {
|
62
|
+
config.paypalButton.removeAttribute('disabled');
|
63
|
+
config.paypalButton.addEventListener('click', function(event) {
|
64
|
+
config.paypalInstance.tokenize(config.paypalOptions, callback);
|
65
|
+
}, false);
|
66
|
+
},
|
67
|
+
|
68
|
+
/**
|
69
|
+
* Default callback function for when tokenization completes
|
70
|
+
*
|
71
|
+
* @param {object|null} tokenizeErr - The error returned by Braintree on failure
|
72
|
+
* @param {object} payload - The payload returned by Braintree on success
|
73
|
+
*/
|
74
|
+
PaypalButton.prototype.tokenizeCallback = function(tokenizeErr, payload) {
|
75
|
+
if (tokenizeErr) {
|
76
|
+
console.error('Error tokenizing:', tokenizeErr);
|
77
|
+
} else {
|
78
|
+
var params = this.transactionParams(payload);
|
79
|
+
|
80
|
+
Spree.ajax({
|
81
|
+
url: Spree.pathFor("solidus_paypal_braintree/transactions"),
|
82
|
+
type: 'POST',
|
83
|
+
dataType: 'json',
|
84
|
+
data: params,
|
85
|
+
success: function(response) {
|
86
|
+
window.location.href = response.redirectUrl;
|
87
|
+
},
|
88
|
+
error: function(xhr) {
|
89
|
+
console.error("Error submitting transaction")
|
90
|
+
},
|
91
|
+
});
|
92
|
+
}
|
93
|
+
};
|
94
|
+
|
95
|
+
/**
|
96
|
+
* Assigns a new callback function for when tokenization completes
|
97
|
+
*
|
98
|
+
* @callback callback - The callback function to assign
|
99
|
+
*/
|
100
|
+
PaypalButton.prototype.setTokenizeCallback = function(callback) {
|
101
|
+
this.tokenizeCallback = callback;
|
102
|
+
};
|
103
|
+
|
104
|
+
/**
|
105
|
+
* Builds the transaction parameters to submit to Solidus for the given
|
106
|
+
* payload returned by Braintree
|
107
|
+
*
|
108
|
+
* @param {object} payload - The payload returned by Braintree after tokenization
|
109
|
+
*/
|
110
|
+
PaypalButton.prototype.transactionParams = function(payload) {
|
111
|
+
return {
|
112
|
+
"payment_method_id" : this.paymentMethodId,
|
113
|
+
"transaction" : {
|
114
|
+
"email" : payload.details.email,
|
115
|
+
"phone" : payload.details.phone,
|
116
|
+
"nonce" : payload.nonce,
|
117
|
+
"payment_type" : payload.type,
|
118
|
+
"address_attributes" : this.addressParams(payload)
|
119
|
+
}
|
120
|
+
}
|
121
|
+
};
|
122
|
+
|
123
|
+
/**
|
124
|
+
* Builds the address parameters to submit to Solidus using the payload
|
125
|
+
* returned by Braintree
|
126
|
+
*
|
127
|
+
* @param {object} payload - The payload returned by Braintree after tokenization
|
128
|
+
*/
|
129
|
+
PaypalButton.prototype.addressParams = function(payload) {
|
130
|
+
if (payload.details.shippingAddress.recipientName) {
|
131
|
+
var first_name = payload.details.shippingAddress.recipientName.split(" ")[0];
|
132
|
+
var last_name = payload.details.shippingAddress.recipientName.split(" ")[1];
|
133
|
+
}
|
134
|
+
if (first_name == null || last_name == null) {
|
135
|
+
var first_name = payload.details.firstName;
|
136
|
+
var last_name = payload.details.lastName;
|
137
|
+
}
|
138
|
+
|
139
|
+
return {
|
140
|
+
"first_name" : first_name,
|
141
|
+
"last_name" : last_name,
|
142
|
+
"address_line_1" : payload.details.shippingAddress.line1,
|
143
|
+
"address_line_2" : payload.details.shippingAddress.line2,
|
144
|
+
"city" : payload.details.shippingAddress.city,
|
145
|
+
"state_code" : payload.details.shippingAddress.state,
|
146
|
+
"zip" : payload.details.shippingAddress.postalCode,
|
147
|
+
"country_code" : payload.details.shippingAddress.countryCode
|
148
|
+
}
|
149
|
+
};
|
150
|
+
|
151
|
+
$(document).ready(function() {
|
152
|
+
if (document.getElementById("empty-cart")) {
|
153
|
+
$.when(
|
154
|
+
$.getScript("https://js.braintreegateway.com/web/3.9.0/js/client.min.js"),
|
155
|
+
$.getScript("https://js.braintreegateway.com/web/3.9.0/js/paypal.min.js"),
|
156
|
+
$.getScript("https://js.braintreegateway.com/web/3.9.0/js/data-collector.min.js"),
|
157
|
+
$.Deferred(function( deferred ){
|
158
|
+
$( deferred.resolve );
|
159
|
+
})
|
160
|
+
).done(function() {
|
161
|
+
$('<script/>').attr({
|
162
|
+
'data-merchant' : "braintree",
|
163
|
+
'data-id' : "paypal-button",
|
164
|
+
'data-button' : "checkout",
|
165
|
+
'data-color' : "blue",
|
166
|
+
'data-size' : "medium",
|
167
|
+
'data-shape' : "pill",
|
168
|
+
'data-button_type' : "button",
|
169
|
+
'data-button_disabled' : "true"
|
170
|
+
}).
|
171
|
+
load(function() {
|
172
|
+
var button = new PaypalButton(document.querySelector("#paypal-button"));
|
173
|
+
button.initialize({
|
174
|
+
flow: 'vault',
|
175
|
+
enableShippingAddress: true,
|
176
|
+
});
|
177
|
+
}).
|
178
|
+
insertAfter("#content").
|
179
|
+
attr('src', 'https://www.paypalobjects.com/api/button.js?')
|
180
|
+
});
|
181
|
+
}
|
182
|
+
});
|
@@ -0,0 +1,188 @@
|
|
1
|
+
//= require spree/braintree_hosted_form
|
2
|
+
|
3
|
+
window.SolidusPaypalBraintree = {
|
4
|
+
APPLE_PAY_API_VERSION: 1,
|
5
|
+
|
6
|
+
// Override to provide your own error messages.
|
7
|
+
braintreeErrorHandle: function(braintreeError) {
|
8
|
+
var $contentContainer = $("#content");
|
9
|
+
var $flash = $("<div class='flash error'>" + braintreeError.name + ": " + braintreeError.message + "</div>");
|
10
|
+
$contentContainer.prepend($flash);
|
11
|
+
|
12
|
+
$flash.show().delay(5000).fadeOut(500);
|
13
|
+
},
|
14
|
+
|
15
|
+
fetchToken: function(tokenCallback) {
|
16
|
+
Spree.ajax({
|
17
|
+
dataType: 'json',
|
18
|
+
type: 'POST',
|
19
|
+
url: Spree.pathFor('solidus_paypal_braintree/client_token'),
|
20
|
+
success: function(response) {
|
21
|
+
tokenCallback(response.client_token, response.payment_method_id);
|
22
|
+
},
|
23
|
+
error: function(xhr) {
|
24
|
+
console.error("Error fetching braintree token");
|
25
|
+
}
|
26
|
+
});
|
27
|
+
},
|
28
|
+
|
29
|
+
initialize: function(authToken, clientReadyCallback) {
|
30
|
+
braintree.client.create({
|
31
|
+
authorization: authToken
|
32
|
+
}, function (clientErr, clientInstance) {
|
33
|
+
if (clientErr) {
|
34
|
+
console.error('Error creating client:', clientErr);
|
35
|
+
return;
|
36
|
+
}
|
37
|
+
clientReadyCallback(clientInstance);
|
38
|
+
});
|
39
|
+
},
|
40
|
+
|
41
|
+
initializeWithDataCollector: function(authToken, clientReadyCallback) {
|
42
|
+
braintree.client.create({
|
43
|
+
authorization: authToken
|
44
|
+
}, function (clientErr, clientInstance) {
|
45
|
+
braintree.dataCollector.create({
|
46
|
+
client: clientInstance,
|
47
|
+
paypal: true
|
48
|
+
}, function (err, dataCollectorInstance) {
|
49
|
+
if (err) {
|
50
|
+
console.error('Error creating data collector:', err);
|
51
|
+
return;
|
52
|
+
}
|
53
|
+
});
|
54
|
+
if (clientErr) {
|
55
|
+
console.error('Error creating client:', clientErr);
|
56
|
+
return;
|
57
|
+
}
|
58
|
+
clientReadyCallback(clientInstance);
|
59
|
+
});
|
60
|
+
},
|
61
|
+
|
62
|
+
setupApplePay: function(braintreeClient, merchantId, readyCallback) {
|
63
|
+
if(window.ApplePaySession && location.protocol == "https:") {
|
64
|
+
var promise = ApplePaySession.canMakePaymentsWithActiveCard(merchantId);
|
65
|
+
promise.then(function (canMakePayments) {
|
66
|
+
if (canMakePayments) {
|
67
|
+
braintree.applePay.create({
|
68
|
+
client: braintreeClient
|
69
|
+
}, function (applePayErr, applePayInstance) {
|
70
|
+
if (applePayErr) {
|
71
|
+
console.error("Error creating ApplePay:", applePayErr);
|
72
|
+
return;
|
73
|
+
}
|
74
|
+
readyCallback(applePayInstance);
|
75
|
+
});
|
76
|
+
}
|
77
|
+
});
|
78
|
+
};
|
79
|
+
},
|
80
|
+
|
81
|
+
/* Initializes and begins the ApplePay session
|
82
|
+
*
|
83
|
+
* @param config Configuration settings for the session
|
84
|
+
* @param config.applePayInstance {object} The instance returned from applePay.create
|
85
|
+
* @param config.storeName {String} The name of the store
|
86
|
+
* @param config.paymentRequest {object} The payment request to submit
|
87
|
+
* @param config.currentUserEmail {String|undefined} The active user's email
|
88
|
+
* @param config.paymentMethodId {Integer} The SolidusPaypalBraintree::Gateway id
|
89
|
+
*/
|
90
|
+
initializeApplePaySession: function(config, sessionCallback) {
|
91
|
+
|
92
|
+
var requiredFields = ['postalAddress', 'phone'];
|
93
|
+
|
94
|
+
if (!config.currentUserEmail) {
|
95
|
+
requiredFields.push('email');
|
96
|
+
}
|
97
|
+
|
98
|
+
config.paymentRequest['requiredShippingContactFields'] = requiredFields
|
99
|
+
var paymentRequest = config.applePayInstance.createPaymentRequest(config.paymentRequest);
|
100
|
+
|
101
|
+
var session = new ApplePaySession(SolidusPaypalBraintree.APPLE_PAY_API_VERSION, paymentRequest);
|
102
|
+
session.onvalidatemerchant = function (event) {
|
103
|
+
config.applePayInstance.performValidation({
|
104
|
+
validationURL: event.validationURL,
|
105
|
+
displayName: config.storeName,
|
106
|
+
}, function (validationErr, merchantSession) {
|
107
|
+
if (validationErr) {
|
108
|
+
console.error('Error validating Apple Pay:', validationErr);
|
109
|
+
session.abort();
|
110
|
+
return;
|
111
|
+
};
|
112
|
+
session.completeMerchantValidation(merchantSession);
|
113
|
+
});
|
114
|
+
};
|
115
|
+
|
116
|
+
session.onpaymentauthorized = function (event) {
|
117
|
+
config.applePayInstance.tokenize({
|
118
|
+
token: event.payment.token
|
119
|
+
}, function (tokenizeErr, payload) {
|
120
|
+
if (tokenizeErr) {
|
121
|
+
console.error('Error tokenizing Apple Pay:', tokenizeErr);
|
122
|
+
session.completePayment(ApplePaySession.STATUS_FAILURE);
|
123
|
+
}
|
124
|
+
|
125
|
+
var contact = event.payment.shippingContact;
|
126
|
+
|
127
|
+
Spree.ajax({
|
128
|
+
data: SolidusPaypalBraintree.buildTransaction(payload, config, contact),
|
129
|
+
dataType: 'json',
|
130
|
+
type: 'POST',
|
131
|
+
url: Spree.pathFor('solidus_paypal_braintree/transactions'),
|
132
|
+
success: function(response) {
|
133
|
+
session.completePayment(ApplePaySession.STATUS_SUCCESS);
|
134
|
+
window.location.replace(response.redirectUrl);
|
135
|
+
},
|
136
|
+
error: function(xhr) {
|
137
|
+
if (xhr.status === 422) {
|
138
|
+
var errors = xhr.responseJSON.errors
|
139
|
+
|
140
|
+
if (errors && errors["Address"]) {
|
141
|
+
session.completePayment(ApplePaySession.STATUS_INVALID_SHIPPING_POSTAL_ADDRESS);
|
142
|
+
} else {
|
143
|
+
session.completePayment(ApplePaySession.STATUS_FAILURE);
|
144
|
+
}
|
145
|
+
}
|
146
|
+
}
|
147
|
+
});
|
148
|
+
|
149
|
+
});
|
150
|
+
};
|
151
|
+
|
152
|
+
sessionCallback(session);
|
153
|
+
|
154
|
+
session.begin();
|
155
|
+
},
|
156
|
+
|
157
|
+
buildTransaction: function(payload, config, shippingContact) {
|
158
|
+
return {
|
159
|
+
transaction: {
|
160
|
+
nonce: payload.nonce,
|
161
|
+
phone: shippingContact.phoneNumber,
|
162
|
+
email: config.currentUserEmail || shippingContact.emailAddress,
|
163
|
+
payment_type: payload.type,
|
164
|
+
address_attributes: SolidusPaypalBraintree.buildAddress(shippingContact)
|
165
|
+
},
|
166
|
+
payment_method_id: config.paymentMethodId
|
167
|
+
};
|
168
|
+
},
|
169
|
+
|
170
|
+
buildAddress: function(shippingContact) {
|
171
|
+
var addressHash = {
|
172
|
+
country_name: shippingContact.country,
|
173
|
+
country_code: shippingContact.countryCode,
|
174
|
+
first_name: shippingContact.givenName,
|
175
|
+
last_name: shippingContact.familyName,
|
176
|
+
state_code: shippingContact.administrativeArea,
|
177
|
+
city: shippingContact.locality,
|
178
|
+
zip: shippingContact.postalCode,
|
179
|
+
address_line_1: shippingContact.addressLines[0]
|
180
|
+
};
|
181
|
+
|
182
|
+
if(shippingContact.addressLines.length > 1) {
|
183
|
+
addressHash['address_line_2'] = shippingContact.addressLines[1];
|
184
|
+
}
|
185
|
+
|
186
|
+
return addressHash;
|
187
|
+
}
|
188
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
/*
|
2
|
+
Placeholder manifest file.
|
3
|
+
the installer will append this file to the app vendored assets here: 'vendor/assets/stylesheets/spree/backend/all.css'
|
4
|
+
*/
|
5
|
+
|
6
|
+
// Set Bootstrap variables if they're not already defined
|
7
|
+
// e.g if we're on Solidus < v1.4
|
8
|
+
$input-border-color: #cee1f4 !default;
|
9
|
+
$border-radius: 3px !default;
|
10
|
+
$input-color: #5498DA !default;
|
11
|
+
$brand-danger: #C60F13 !default;
|
12
|
+
$brand-success: #9FC820 !default;
|
13
|
+
|
14
|
+
.braintree-hosted-fields .input {
|
15
|
+
border: 1px solid $input-border-color;
|
16
|
+
border-radius: $border-radius;
|
17
|
+
color: $input-color;
|
18
|
+
height: 30px;
|
19
|
+
padding: 7px 10px;
|
20
|
+
|
21
|
+
&.braintree-hosted-fields-invalid {
|
22
|
+
border-color: $brand-danger;
|
23
|
+
}
|
24
|
+
|
25
|
+
&.braintree-hosted-fields-valid {
|
26
|
+
border-color: $brand-success;
|
27
|
+
}
|
28
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
/*
|
2
|
+
Placeholder manifest file.
|
3
|
+
the installer will append this file to the app vendored assets here: 'vendor/assets/stylesheets/spree/frontend/all.css'
|
4
|
+
*/
|
5
|
+
|
6
|
+
.braintree-hosted-fields .input {
|
7
|
+
border: 1px solid #d9d9db;
|
8
|
+
border-radius: 3px;
|
9
|
+
color: #5498DA;
|
10
|
+
height: 30px;
|
11
|
+
padding: 5px 10px;
|
12
|
+
}
|
13
|
+
|
14
|
+
.paypal-button-widget .paypal-button:hover {
|
15
|
+
background: transparent;
|
16
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module SolidusPaypalBraintree
|
2
|
+
class ClientTokensController < Spree::Api::BaseController
|
3
|
+
skip_before_action :authenticate_user
|
4
|
+
|
5
|
+
before_action :load_gateway
|
6
|
+
|
7
|
+
def create
|
8
|
+
render json: { client_token: @gateway.generate_token, payment_method_id: @gateway.id }
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def load_gateway
|
14
|
+
if params[:payment_method_id]
|
15
|
+
@gateway = ::SolidusPaypalBraintree::Gateway.find_by!(id: params[:payment_method_id])
|
16
|
+
else
|
17
|
+
@gateway = ::SolidusPaypalBraintree::Gateway.find_by!(active: true)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module BraintreeAdminHelper
|
2
|
+
# Returns a link to the Braintree web UI for the given Braintree payment
|
3
|
+
def braintree_transaction_link(payment)
|
4
|
+
environment = payment.payment_method.preferred_environment == 'sandbox' ? 'sandbox' : 'www'
|
5
|
+
merchant_id = payment.payment_method.preferred_merchant_id
|
6
|
+
response_code = payment.response_code
|
7
|
+
|
8
|
+
return unless response_code.present?
|
9
|
+
return response_code unless merchant_id.present?
|
10
|
+
|
11
|
+
link_to(
|
12
|
+
response_code,
|
13
|
+
"https://#{environment}.braintreegateway.com/merchants/#{merchant_id}/transactions/#{response_code}",
|
14
|
+
title: 'Show payment on Braintree',
|
15
|
+
target: '_blank'
|
16
|
+
)
|
17
|
+
end
|
18
|
+
end
|