Skip to content

Commit

Permalink
Release braintree-web 3.110.0 source
Browse files Browse the repository at this point in the history
  • Loading branch information
braintreeps committed Oct 15, 2024
1 parent ee0c13a commit 7f9146b
Show file tree
Hide file tree
Showing 16 changed files with 514 additions and 229 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# CHANGELOG

## 3.110.0

- SEPA
- Add support for new full page redirect flow

## 3.109.0

- PayPal Checkout
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "braintree-web",
"version": "3.109.0",
"version": "3.110.0",
"license": "MIT",
"main": "src/index.js",
"private": true,
Expand Down
7 changes: 5 additions & 2 deletions src/client/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ var BraintreeError = require("../lib/braintree-error");
var convertToBraintreeError = require("../lib/convert-to-braintree-error");
var getGatewayConfiguration = require("./get-configuration").getConfiguration;
var createAuthorizationData = require("../lib/create-authorization-data");
var addMetadata = require("../lib/add-metadata");
var metadata = require("../lib/add-metadata");
var wrapPromise = require("@braintree/wrap-promise");
var once = require("../lib/once");
var deferred = require("../lib/deferred");
Expand Down Expand Up @@ -321,7 +321,10 @@ Client.prototype.request = function (options, callback) {
if (api === "clientApi") {
baseUrl = self._clientApiBaseUrl;

requestOptions.data = addMetadata(self._configuration, options.data);
requestOptions.data = metadata.addMetadata(
self._configuration,
options.data
);
} else if (api === "graphQLApi") {
baseUrl =
GRAPHQL_URLS[self._configuration.gatewayConfiguration.environment];
Expand Down
36 changes: 35 additions & 1 deletion src/lib/add-metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,38 @@ function addMetadata(configuration, data) {
return attrs;
}

module.exports = addMetadata;
function addEventMetadata(clientInstanceOrPromise) {
var configuration = clientInstanceOrPromise.getConfiguration();
var authAttrs = createAuthorizationData(configuration.authorization).attrs;
var isProd = configuration.gatewayConfiguration.environment === "production";

/* eslint-disable camelcase */
var metadata = {
api_integration_type: configuration.analyticsMetadata.integrationType,
app_id: window.location.host,
c_sdk_ver: constants.VERSION,
component: "braintreeclientsdk",
merchant_sdk_env: isProd ? "production" : "sandbox",
merchant_id: configuration.gatewayConfiguration.merchantId,
event_source: "web",
platform: constants.PLATFORM,
platform_version: window.navigator.userAgent,
session_id: configuration.analyticsMetadata.sessionId,
client_session_id: configuration.analyticsMetadata.sessionId,
tenant_name: "braintree",
};

if (authAttrs.tokenizationKey) {
metadata.tokenization_key = authAttrs.tokenizationKey;
} else {
metadata.auth_fingerprint = authAttrs.authorizationFingerprint;
}
/* eslint-enable camelcase */

return metadata;
}

module.exports = {
addMetadata: addMetadata,
addEventMetadata: addEventMetadata,
};
51 changes: 28 additions & 23 deletions src/lib/analytics.js
Original file line number Diff line number Diff line change
@@ -1,52 +1,57 @@
"use strict";

var constants = require("./constants");
var addMetadata = require("./add-metadata");
var metadata = require("./add-metadata");

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

return Promise.resolve(clientInstanceOrPromise)
.then(function (client) {
var timestampInPromise = Date.now();
var configuration = client.getConfiguration();
var request = client._request;
var url = configuration.gatewayConfiguration.analytics.url;
var url = constants.ANALYTICS_URL;
var qualifiedEvent = constants.ANALYTICS_PREFIX + eventName;
var configuration = client.getConfiguration();
var isProd =
configuration.gatewayConfiguration.environment === "production";
var data = {
analytics: [
{
kind: constants.ANALYTICS_PREFIX + kind,
isAsync:
Math.floor(timestampInPromise / 1000) !==
Math.floor(timestamp / 1000),
events: [],
tracking: [],
};
var trackingMeta = metadata.addEventMetadata(client, data);

trackingMeta.event_name = qualifiedEvent; // eslint-disable-line camelcase
trackingMeta.t = timestamp; // eslint-disable-line camelcase

data.events = [
{
level: "info",
event: qualifiedEvent,
payload: {
env: isProd ? "production" : "sandbox",
timestamp: timestamp,
},
],
};
},
];
data.tracking = [trackingMeta];

request(
return request(
{
url: url,
method: "post",
data: addMetadata(configuration, data),
data: data,
timeout: constants.ANALYTICS_REQUEST_TIMEOUT_MS,
},
callback
);
})
.catch(function (err) {
// for all non-test cases, we don't provide a callback,
// so this error will always be swallowed. In this case,
// that's fine, it should only error when the deferred
// client fails to set up, in which case we don't want
// that error to report over and over again via these
// deferred analytics events
if (callback) {
callback(err);
}
});
}

module.exports = {
sendEvent: sendAnalyticsEvent,
sendEvent: sendPaypalEvent,
};
1 change: 1 addition & 0 deletions src/lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ if (process.env.BRAINTREE_JS_ENV === "development") {
module.exports = {
ANALYTICS_PREFIX: PLATFORM + ".",
ANALYTICS_REQUEST_TIMEOUT_MS: 2000,
ANALYTICS_URL: "https://www.paypal.com/xoplatform/logger/api/logger",
ASSETS_URLS: ASSETS_URLS,
CLIENT_API_URLS: CLIENT_API_URLS,
FRAUDNET_SOURCE: "BRAINTREE_SIGNIN",
Expand Down
39 changes: 39 additions & 0 deletions src/sepa/external/mandate.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ var useMin = require("../../lib/use-min");
var billingAddressOptions =
require("../shared/constants").BILLING_ADDRESS_OPTIONS;
var snakeCaseToCamelCase = require("../../lib/snake-case-to-camel-case");
var assign = require("../../lib/assign").assign;

var POPUP_WIDTH = 400;
var POPUP_HEIGHT = 570;
Expand Down Expand Up @@ -165,6 +166,10 @@ function openPopup(client, options) {
});
}

function redirectPage(approvalUrl) {
window.location.href = approvalUrl;
}

function mandateApproved(params) {
return params && params.success;
}
Expand Down Expand Up @@ -244,10 +249,44 @@ function handleApproval(client, options) {
});
}

function handleApprovalForFullPageRedirect(client, options) {
return client
.request({
api: "clientApi",
method: "get",
endpoint: "sepa_debit/" + options.cart_id,
})
.then(function (response) {
var payload = response.sepaDebitMandateDetail;

analytics.sendEvent(client, "sepa.redirect.mandate.approved");

assign(options, {
last4: payload.last4,
customerId: payload.merchantOrPartnerCustomerId,
mandateType: payload.mandateType,
bankReferenceToken: payload.bankReferenceToken,
});

return handleApproval(client, options);
})
.then(function (response) {
analytics.sendEvent(client, "sepa.redirect.tokenization.success");

return response;
})
.catch(function () {
analytics.sendEvent(client, "sepa.redirect.handle-approval.failed");
throw new BraintreeError(sepaErrors.SEPA_TRANSACTION_FAILED);
});
}

module.exports = {
createMandate: createMandate,
openPopup: openPopup,
handleApproval: handleApproval,
POPUP_WIDTH: POPUP_WIDTH,
POPUP_HEIGHT: POPUP_HEIGHT,
redirectPage: redirectPage,
handleApprovalForFullPageRedirect: handleApprovalForFullPageRedirect,
};
31 changes: 27 additions & 4 deletions src/sepa/external/sepa.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,17 @@ function SEPA(options) {
this._assetsUrl =
getConfiguration.gatewayConfiguration.assetsUrl + "/web/" + VERSION;
this._isDebug = getConfiguration.isDebug;
this._returnUrl = this._assetsUrl + "/html/redirect-frame.html?success=1";
this._cancelUrl = this._assetsUrl + "/html/redirect-frame.html?cancel=1";
if (options.redirectUrl) {
this._returnUrl = options.redirectUrl;
this._cancelUrl = options.redirectUrl + "?cancel=1";
this._isRedirectFlow = true;
} else {
this._returnUrl = this._assetsUrl + "/html/redirect-frame.html?success=1";
this._cancelUrl = this._assetsUrl + "/html/redirect-frame.html?cancel=1";
}
if (options.tokenizePayload) {
this.tokenizePayload = options.tokenizePayload;
}

analytics.sendEvent(this._client, "sepa.component.initialized");
}
Expand Down Expand Up @@ -69,6 +78,7 @@ function SEPA(options) {
*/
SEPA.prototype.tokenize = function (options) {
var self = this;
var popupPromise;
var createMandateOptions = assign(
{ cancelUrl: self._cancelUrl, returnUrl: self._returnUrl },
options
Expand All @@ -90,18 +100,31 @@ SEPA.prototype.tokenize = function (options) {
);
}

return mandates
popupPromise = mandates
.createMandate(self._client, createMandateOptions)
.then(function (mandateResponse) {
analytics.sendEvent(self._client, "sepa.create-mandate.success");

if (self._isRedirectFlow) {
return mandates.redirectPage(mandateResponse.approvalUrl);
}

options.last4 = mandateResponse.last4;
options.bankReferenceToken = mandateResponse.bankReferenceToken;

return mandates.openPopup(self._client, {
approvalUrl: mandateResponse.approvalUrl,
assetsUrl: self._assetsUrl,
});
})
});

// Must be outside of .then() to return from top-level function
if (self._isRedirectFlow) {
return Promise.resolve();
}

// At this point, we know the promise came from the popup flow
return popupPromise
.then(function () {
analytics.sendEvent(self._client, "sepa.mandate.approved");

Expand Down
35 changes: 34 additions & 1 deletion src/sepa/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ var createDeferredClient = require("../lib/create-deferred-client");
var basicComponentVerification = require("../lib/basic-component-verification");
var wrapPromise = require("@braintree/wrap-promise");
var VERSION = process.env.npm_package_version;
var parse = require("../lib/querystring").parse;
var assign = require("../lib/assign").assign;
var mandate = require("./external/mandate");

/**
* @static
Expand All @@ -16,6 +19,7 @@ var VERSION = process.env.npm_package_version;
* @param {Client} [options.client] A {@link Client} instance.
* @param {string} [options.authorization] A tokenizationKey or clientToken. Can be used in place of `options.client`.
* @param {boolean} [options.debug] A debug flag.
* @param {string} [options.redirectUrl] When provided, triggers full page redirect flow instead of popup flow.
* @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}.
* @returns {Promise<void|error>} Returns the SEPA instance.
* @example
Expand All @@ -38,6 +42,7 @@ var VERSION = process.env.npm_package_version;

function create(options) {
var name = "SEPA";
var params = parse(window.location.href);

return basicComponentVerification
.verify({
Expand All @@ -57,9 +62,37 @@ function create(options) {
.then(function (client) {
options.client = client;

analytics.sendEvent(options.client, "sepa.client.initialized");
analytics.sendEvent(client, "sepa.client.initialized");

return new SEPA(options);
})
.then(function (sepaInstance) {
// This cart_id is actually a V2 orderId
var redirectComplete =
params.success && params.success === "true" && params.cart_id;

if (redirectComplete) {
options = assign(options, params);

// Pick up redirect flow where it left off
return mandate
.handleApprovalForFullPageRedirect(options.client, options)
.then(function (payload) {
sepaInstance.tokenizePayload = payload;

return sepaInstance;
})
.catch(function (err) {
console.error("Problem while finishing tokenizing: ", err);
});
} else if (params.cancel) {
analytics.sendEvent(
options.client,
"sepa.redirect.customer-canceled.failed"
);
}

return sepaInstance;
});
}

Expand Down
14 changes: 6 additions & 8 deletions test/client/unit/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -472,15 +472,13 @@ describe("Client", () => {
it("calls driver with client for source in _meta if source is not provided", () => {
const client = new Client(fakeConfiguration());

jest.spyOn(client, "_request").mockReturnValue(null);
client.request(
{
endpoint: "payment_methods",
method: "get",
},
() => {}
);
jest.spyOn(client, "_request").mockReturnValue(null); // yieldsAsync
client.request({
endpoint: "payment_methods",
method: "get",
});

expect(client._request.mock.calls).not.toEqual([]);
expect(client._request.mock.calls[0][0]).toMatchObject({
data: { _meta: { source: "client" } },
});
Expand Down
Loading

0 comments on commit 7f9146b

Please sign in to comment.