Skip to content

Commit 7f9146b

Browse files
committed
Release braintree-web 3.110.0 source
1 parent ee0c13a commit 7f9146b

File tree

16 files changed

+514
-229
lines changed

16 files changed

+514
-229
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# CHANGELOG
22

3+
## 3.110.0
4+
5+
- SEPA
6+
- Add support for new full page redirect flow
7+
38
## 3.109.0
49

510
- PayPal Checkout

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "braintree-web",
3-
"version": "3.109.0",
3+
"version": "3.110.0",
44
"license": "MIT",
55
"main": "src/index.js",
66
"private": true,

src/client/client.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ var BraintreeError = require("../lib/braintree-error");
99
var convertToBraintreeError = require("../lib/convert-to-braintree-error");
1010
var getGatewayConfiguration = require("./get-configuration").getConfiguration;
1111
var createAuthorizationData = require("../lib/create-authorization-data");
12-
var addMetadata = require("../lib/add-metadata");
12+
var metadata = require("../lib/add-metadata");
1313
var wrapPromise = require("@braintree/wrap-promise");
1414
var once = require("../lib/once");
1515
var deferred = require("../lib/deferred");
@@ -321,7 +321,10 @@ Client.prototype.request = function (options, callback) {
321321
if (api === "clientApi") {
322322
baseUrl = self._clientApiBaseUrl;
323323

324-
requestOptions.data = addMetadata(self._configuration, options.data);
324+
requestOptions.data = metadata.addMetadata(
325+
self._configuration,
326+
options.data
327+
);
325328
} else if (api === "graphQLApi") {
326329
baseUrl =
327330
GRAPHQL_URLS[self._configuration.gatewayConfiguration.environment];

src/lib/add-metadata.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,38 @@ function addMetadata(configuration, data) {
2929
return attrs;
3030
}
3131

32-
module.exports = addMetadata;
32+
function addEventMetadata(clientInstanceOrPromise) {
33+
var configuration = clientInstanceOrPromise.getConfiguration();
34+
var authAttrs = createAuthorizationData(configuration.authorization).attrs;
35+
var isProd = configuration.gatewayConfiguration.environment === "production";
36+
37+
/* eslint-disable camelcase */
38+
var metadata = {
39+
api_integration_type: configuration.analyticsMetadata.integrationType,
40+
app_id: window.location.host,
41+
c_sdk_ver: constants.VERSION,
42+
component: "braintreeclientsdk",
43+
merchant_sdk_env: isProd ? "production" : "sandbox",
44+
merchant_id: configuration.gatewayConfiguration.merchantId,
45+
event_source: "web",
46+
platform: constants.PLATFORM,
47+
platform_version: window.navigator.userAgent,
48+
session_id: configuration.analyticsMetadata.sessionId,
49+
client_session_id: configuration.analyticsMetadata.sessionId,
50+
tenant_name: "braintree",
51+
};
52+
53+
if (authAttrs.tokenizationKey) {
54+
metadata.tokenization_key = authAttrs.tokenizationKey;
55+
} else {
56+
metadata.auth_fingerprint = authAttrs.authorizationFingerprint;
57+
}
58+
/* eslint-enable camelcase */
59+
60+
return metadata;
61+
}
62+
63+
module.exports = {
64+
addMetadata: addMetadata,
65+
addEventMetadata: addEventMetadata,
66+
};

src/lib/analytics.js

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,57 @@
11
"use strict";
22

33
var constants = require("./constants");
4-
var addMetadata = require("./add-metadata");
4+
var metadata = require("./add-metadata");
55

6-
function sendAnalyticsEvent(clientInstanceOrPromise, kind, callback) {
7-
var timestamp = Date.now(); // milliseconds
6+
function sendPaypalEvent(clientInstanceOrPromise, eventName, callback) {
7+
var timestamp = Date.now();
88

99
return Promise.resolve(clientInstanceOrPromise)
1010
.then(function (client) {
11-
var timestampInPromise = Date.now();
12-
var configuration = client.getConfiguration();
1311
var request = client._request;
14-
var url = configuration.gatewayConfiguration.analytics.url;
12+
var url = constants.ANALYTICS_URL;
13+
var qualifiedEvent = constants.ANALYTICS_PREFIX + eventName;
14+
var configuration = client.getConfiguration();
15+
var isProd =
16+
configuration.gatewayConfiguration.environment === "production";
1517
var data = {
16-
analytics: [
17-
{
18-
kind: constants.ANALYTICS_PREFIX + kind,
19-
isAsync:
20-
Math.floor(timestampInPromise / 1000) !==
21-
Math.floor(timestamp / 1000),
18+
events: [],
19+
tracking: [],
20+
};
21+
var trackingMeta = metadata.addEventMetadata(client, data);
22+
23+
trackingMeta.event_name = qualifiedEvent; // eslint-disable-line camelcase
24+
trackingMeta.t = timestamp; // eslint-disable-line camelcase
25+
26+
data.events = [
27+
{
28+
level: "info",
29+
event: qualifiedEvent,
30+
payload: {
31+
env: isProd ? "production" : "sandbox",
2232
timestamp: timestamp,
2333
},
24-
],
25-
};
34+
},
35+
];
36+
data.tracking = [trackingMeta];
2637

27-
request(
38+
return request(
2839
{
2940
url: url,
3041
method: "post",
31-
data: addMetadata(configuration, data),
42+
data: data,
3243
timeout: constants.ANALYTICS_REQUEST_TIMEOUT_MS,
3344
},
3445
callback
3546
);
3647
})
3748
.catch(function (err) {
38-
// for all non-test cases, we don't provide a callback,
39-
// so this error will always be swallowed. In this case,
40-
// that's fine, it should only error when the deferred
41-
// client fails to set up, in which case we don't want
42-
// that error to report over and over again via these
43-
// deferred analytics events
4449
if (callback) {
4550
callback(err);
4651
}
4752
});
4853
}
4954

5055
module.exports = {
51-
sendEvent: sendAnalyticsEvent,
56+
sendEvent: sendPaypalEvent,
5257
};

src/lib/constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ if (process.env.BRAINTREE_JS_ENV === "development") {
3434
module.exports = {
3535
ANALYTICS_PREFIX: PLATFORM + ".",
3636
ANALYTICS_REQUEST_TIMEOUT_MS: 2000,
37+
ANALYTICS_URL: "https://www.paypal.com/xoplatform/logger/api/logger",
3738
ASSETS_URLS: ASSETS_URLS,
3839
CLIENT_API_URLS: CLIENT_API_URLS,
3940
FRAUDNET_SOURCE: "BRAINTREE_SIGNIN",

src/sepa/external/mandate.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ var useMin = require("../../lib/use-min");
88
var billingAddressOptions =
99
require("../shared/constants").BILLING_ADDRESS_OPTIONS;
1010
var snakeCaseToCamelCase = require("../../lib/snake-case-to-camel-case");
11+
var assign = require("../../lib/assign").assign;
1112

1213
var POPUP_WIDTH = 400;
1314
var POPUP_HEIGHT = 570;
@@ -165,6 +166,10 @@ function openPopup(client, options) {
165166
});
166167
}
167168

169+
function redirectPage(approvalUrl) {
170+
window.location.href = approvalUrl;
171+
}
172+
168173
function mandateApproved(params) {
169174
return params && params.success;
170175
}
@@ -244,10 +249,44 @@ function handleApproval(client, options) {
244249
});
245250
}
246251

252+
function handleApprovalForFullPageRedirect(client, options) {
253+
return client
254+
.request({
255+
api: "clientApi",
256+
method: "get",
257+
endpoint: "sepa_debit/" + options.cart_id,
258+
})
259+
.then(function (response) {
260+
var payload = response.sepaDebitMandateDetail;
261+
262+
analytics.sendEvent(client, "sepa.redirect.mandate.approved");
263+
264+
assign(options, {
265+
last4: payload.last4,
266+
customerId: payload.merchantOrPartnerCustomerId,
267+
mandateType: payload.mandateType,
268+
bankReferenceToken: payload.bankReferenceToken,
269+
});
270+
271+
return handleApproval(client, options);
272+
})
273+
.then(function (response) {
274+
analytics.sendEvent(client, "sepa.redirect.tokenization.success");
275+
276+
return response;
277+
})
278+
.catch(function () {
279+
analytics.sendEvent(client, "sepa.redirect.handle-approval.failed");
280+
throw new BraintreeError(sepaErrors.SEPA_TRANSACTION_FAILED);
281+
});
282+
}
283+
247284
module.exports = {
248285
createMandate: createMandate,
249286
openPopup: openPopup,
250287
handleApproval: handleApproval,
251288
POPUP_WIDTH: POPUP_WIDTH,
252289
POPUP_HEIGHT: POPUP_HEIGHT,
290+
redirectPage: redirectPage,
291+
handleApprovalForFullPageRedirect: handleApprovalForFullPageRedirect,
253292
};

src/sepa/external/sepa.js

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,17 @@ function SEPA(options) {
2323
this._assetsUrl =
2424
getConfiguration.gatewayConfiguration.assetsUrl + "/web/" + VERSION;
2525
this._isDebug = getConfiguration.isDebug;
26-
this._returnUrl = this._assetsUrl + "/html/redirect-frame.html?success=1";
27-
this._cancelUrl = this._assetsUrl + "/html/redirect-frame.html?cancel=1";
26+
if (options.redirectUrl) {
27+
this._returnUrl = options.redirectUrl;
28+
this._cancelUrl = options.redirectUrl + "?cancel=1";
29+
this._isRedirectFlow = true;
30+
} else {
31+
this._returnUrl = this._assetsUrl + "/html/redirect-frame.html?success=1";
32+
this._cancelUrl = this._assetsUrl + "/html/redirect-frame.html?cancel=1";
33+
}
34+
if (options.tokenizePayload) {
35+
this.tokenizePayload = options.tokenizePayload;
36+
}
2837

2938
analytics.sendEvent(this._client, "sepa.component.initialized");
3039
}
@@ -69,6 +78,7 @@ function SEPA(options) {
6978
*/
7079
SEPA.prototype.tokenize = function (options) {
7180
var self = this;
81+
var popupPromise;
7282
var createMandateOptions = assign(
7383
{ cancelUrl: self._cancelUrl, returnUrl: self._returnUrl },
7484
options
@@ -90,18 +100,31 @@ SEPA.prototype.tokenize = function (options) {
90100
);
91101
}
92102

93-
return mandates
103+
popupPromise = mandates
94104
.createMandate(self._client, createMandateOptions)
95105
.then(function (mandateResponse) {
96106
analytics.sendEvent(self._client, "sepa.create-mandate.success");
107+
108+
if (self._isRedirectFlow) {
109+
return mandates.redirectPage(mandateResponse.approvalUrl);
110+
}
111+
97112
options.last4 = mandateResponse.last4;
98113
options.bankReferenceToken = mandateResponse.bankReferenceToken;
99114

100115
return mandates.openPopup(self._client, {
101116
approvalUrl: mandateResponse.approvalUrl,
102117
assetsUrl: self._assetsUrl,
103118
});
104-
})
119+
});
120+
121+
// Must be outside of .then() to return from top-level function
122+
if (self._isRedirectFlow) {
123+
return Promise.resolve();
124+
}
125+
126+
// At this point, we know the promise came from the popup flow
127+
return popupPromise
105128
.then(function () {
106129
analytics.sendEvent(self._client, "sepa.mandate.approved");
107130

src/sepa/index.js

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ var createDeferredClient = require("../lib/create-deferred-client");
88
var basicComponentVerification = require("../lib/basic-component-verification");
99
var wrapPromise = require("@braintree/wrap-promise");
1010
var VERSION = process.env.npm_package_version;
11+
var parse = require("../lib/querystring").parse;
12+
var assign = require("../lib/assign").assign;
13+
var mandate = require("./external/mandate");
1114

1215
/**
1316
* @static
@@ -16,6 +19,7 @@ var VERSION = process.env.npm_package_version;
1619
* @param {Client} [options.client] A {@link Client} instance.
1720
* @param {string} [options.authorization] A tokenizationKey or clientToken. Can be used in place of `options.client`.
1821
* @param {boolean} [options.debug] A debug flag.
22+
* @param {string} [options.redirectUrl] When provided, triggers full page redirect flow instead of popup flow.
1923
* @param {callback} [callback] When provided, will be used instead of a promise. First argument is an error object, where the second is an instance of {@link SEPA|SEPA}.
2024
* @returns {Promise<void|error>} Returns the SEPA instance.
2125
* @example
@@ -38,6 +42,7 @@ var VERSION = process.env.npm_package_version;
3842

3943
function create(options) {
4044
var name = "SEPA";
45+
var params = parse(window.location.href);
4146

4247
return basicComponentVerification
4348
.verify({
@@ -57,9 +62,37 @@ function create(options) {
5762
.then(function (client) {
5863
options.client = client;
5964

60-
analytics.sendEvent(options.client, "sepa.client.initialized");
65+
analytics.sendEvent(client, "sepa.client.initialized");
6166

6267
return new SEPA(options);
68+
})
69+
.then(function (sepaInstance) {
70+
// This cart_id is actually a V2 orderId
71+
var redirectComplete =
72+
params.success && params.success === "true" && params.cart_id;
73+
74+
if (redirectComplete) {
75+
options = assign(options, params);
76+
77+
// Pick up redirect flow where it left off
78+
return mandate
79+
.handleApprovalForFullPageRedirect(options.client, options)
80+
.then(function (payload) {
81+
sepaInstance.tokenizePayload = payload;
82+
83+
return sepaInstance;
84+
})
85+
.catch(function (err) {
86+
console.error("Problem while finishing tokenizing: ", err);
87+
});
88+
} else if (params.cancel) {
89+
analytics.sendEvent(
90+
options.client,
91+
"sepa.redirect.customer-canceled.failed"
92+
);
93+
}
94+
95+
return sepaInstance;
6396
});
6497
}
6598

0 commit comments

Comments
 (0)