Skip to content

Commit

Permalink
Release braintree-web 3.100.0 source
Browse files Browse the repository at this point in the history
Co-authored-by: Joe Plukarski <jplukarski@paypal.com>
  • Loading branch information
braintreeps committed Feb 6, 2024
1 parent 288ea4f commit eb5b835
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 233 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# CHANGELOG

# 3.100.0

- 3D Secure
- Remove call to V1 modal as 3DS v1 is unsupported
- Local Payment
- Add support for recurrent local payment

# 3.99.2

- Venmo
Expand Down
2 changes: 1 addition & 1 deletion 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.99.2",
"version": "3.100.0",
"license": "MIT",
"main": "src/index.js",
"private": true,
Expand Down
7 changes: 7 additions & 0 deletions src/local-payment/external/local-payment.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ LocalPayment.prototype._initialize = function () {
* @property {string} surname Last name of the customer.
* @property {string} phone Phone number of the customer.
* @property {string} bic Bank Identification Code of the customer (specific to iDEAL transactions).
* @property {boolean} recurrent Enable recurrent payment.
* @property {string} customerId The customer's id in merchant's system (required for recurrent payments).
* @property {boolean} shippingAddressRequired Indicates whether or not the payment needs to be shipped. For digital goods, this should be false. Defaults to false.
* @property {object} address The shipping address.
* @property {string} address.streetAddress Line 1 of the Address (eg. number, street, etc). An error will occur if this address is not valid.
Expand Down Expand Up @@ -427,11 +429,13 @@ LocalPayment.prototype.startPayment = function (options) {
line2: address.extendedAddress,
lineItems: options.lineItems,
merchantAccountId: self._merchantAccountId,
merchantOrPartnerCustomerId: options.customerId,
payerEmail: options.email,
paymentTypeCountryCode: options.paymentTypeCountryCode,
phone: options.phone,
phoneCountryCode: options.phoneCountryCode,
postalCode: address.postalCode,
recurrent: options.recurrent,
returnUrl: querystring.queryify(
self._assetsUrl +
"/html/local-payment-redirect-frame" +
Expand Down Expand Up @@ -997,6 +1001,9 @@ function hasMissingOption(options) {
if (!options.fallback.buttonText) {
return "fallback.buttonText";
}
if (options.recurrent === true && !options.customerId) {
return "customerId";
}
}

return false;
Expand Down
56 changes: 9 additions & 47 deletions src/three-d-secure/external/frameworks/songbird.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ ExtendedPromise.suppressUnhandledPromiseMessage = true;
function SongbirdFramework(options) {
BaseFramework.call(this, options);

this._useV1Fallback = false;
this._songbirdInitFailed = false;
this._clientMetadata = {
requestedThreeDSecureVersion: "2",
sdkVersion: PLATFORM + "/" + VERSION,
Expand Down Expand Up @@ -131,12 +131,12 @@ SongbirdFramework.prototype.initializeChallengeWithLookupResponse = function (
);
};

SongbirdFramework.prototype.initiateV1Fallback = function (errorType) {
this._useV1Fallback = true;
SongbirdFramework.prototype.handleSongbirdError = function (errorType) {
this._songbirdInitFailed = true;
this._removeSongbirdListeners();
analytics.sendEvent(
this._createPromise,
"three-d-secure.v1-fallback." + errorType
"three-d-secure.cardinal-sdk.songbird-error." + errorType
);

if (this._songbirdPromise) {
Expand Down Expand Up @@ -245,30 +245,6 @@ SongbirdFramework.prototype._addV1IframeToPage = function () {
document.body.appendChild(this._v1Modal);
};

SongbirdFramework.prototype._handleAuthResponseFromV1Fallback = function (
data
) {
this._teardownV1Elements();
this._v1Modal.parentNode.removeChild(this._v1Modal);
this._handleV1AuthResponse(data);
};

SongbirdFramework.prototype._presentChallengeWithV1Fallback = function (
lookupResponse
) {
var self = this;

this._setupV1Elements({
lookupResponse: lookupResponse,
showLoader: true,
handleAuthResponse: function (data) {
self._handleAuthResponseFromV1Fallback(data);
},
});
this._v1Modal = this._createV1IframeModal(this._v1Iframe);
this._addV1IframeToPage();
};

SongbirdFramework.prototype.setupSongbird = function (setupOptions) {
var self = this;
var startTime = Date.now();
Expand Down Expand Up @@ -312,7 +288,7 @@ SongbirdFramework.prototype.setupSongbird = function (setupOptions) {
self._client,
"three-d-secure.cardinal-sdk.init.setup-failed"
);
self.initiateV1Fallback(
self.handleSongbirdError(
"cardinal-sdk-setup-failed." + self._v2SetupFailureReason
);
});
Expand Down Expand Up @@ -417,7 +393,7 @@ SongbirdFramework.prototype._loadCardinalScript = function (setupOptions) {
self._client,
"three-d-secure.cardinal-sdk.init.setup-timeout"
);
self.initiateV1Fallback("cardinal-sdk-setup-timeout");
self.handleSongbirdError("cardinal-sdk-setup-timeout");
}, setupOptions.timeout || INTEGRATION_TIMEOUT_MS);

return assets.loadScript({ src: scriptSource });
Expand Down Expand Up @@ -559,26 +535,14 @@ SongbirdFramework.prototype._createPaymentsValidatedCallback = function () {
return function (data, validatedJwt) {
var formattedError;

if (self._useV1Fallback) {
// TODO since we've removed the listeners for the payments validated callback when initiating the v1 fallback,
// we should never get to this point. Leave this analtyics event in for now and review if that is indeed the
// case before removing this block.
analytics.sendEvent(
self._createPromise,
"three-d-secure.verification-flow.cardinal-sdk.payments-validated-callback-called-in-v1-fallback-flow"
);

return;
}

analytics.sendEvent(
self._createPromise,
"three-d-secure.verification-flow.cardinal-sdk.action-code." +
data.ActionCode.toLowerCase()
);

if (!self._verifyCardPromisePlus) {
self.initiateV1Fallback(
self.handleSongbirdError(
"cardinal-sdk-setup-error.number-" + data.ErrorNumber
);

Expand Down Expand Up @@ -762,10 +726,8 @@ SongbirdFramework.prototype._onLookupComplete = function (

SongbirdFramework.prototype._presentChallenge = function (lookupResponse) {
// transactionId is required for the Songbird flow, so if it
// does not exist, we fallback to the 3ds v1 flow
if (this._useV1Fallback || !lookupResponse.lookup.transactionId) {
this._presentChallengeWithV1Fallback(lookupResponse.lookup);

// does not exist, we just return
if (this._songbirdInitFailed || !lookupResponse.lookup.transactionId) {
return;
}

Expand Down
85 changes: 85 additions & 0 deletions test/local-payment/unit/external/local-payment.js
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,8 @@ describe("LocalPayment", () => {
paymentTypeCountryCode: "PL",
amount: "10.00",
intent: "sale",
recurrent: undefined,
merchantOrPartnerCustomerId: undefined,
billingAddress: {
line1: undefined,
line2: undefined,
Expand Down Expand Up @@ -601,6 +603,8 @@ describe("LocalPayment", () => {
paymentTypeCountryCode: "NL",
amount: "10.00",
intent: "sale",
recurrent: undefined,
merchantOrPartnerCustomerId: undefined,
billingAddress: {
line1: "Prinzregentenstr 99991658",
line2: undefined,
Expand Down Expand Up @@ -666,6 +670,8 @@ describe("LocalPayment", () => {
paymentTypeCountryCode: "NL",
amount: "10.00",
intent: "sale",
recurrent: undefined,
merchantOrPartnerCustomerId: undefined,
billingAddress: {
line1: "Prinzregentenstr 99991658",
line2: undefined,
Expand Down Expand Up @@ -937,6 +943,18 @@ describe("LocalPayment", () => {
});
});

it("errors when no customerId param provided for recurrent payment", () => {
testContext.options.recurrent = true;

return testContext.localPayment
.startPayment(testContext.options)
.catch(({ code }) => {
expect(code).toBe(
"LOCAL_PAYMENT_START_PAYMENT_MISSING_REQUIRED_OPTION"
);
});
});

it("errors when authorization is already in progress", () => {
testContext.localPayment._authorizationInProgress = true;

Expand Down Expand Up @@ -967,6 +985,71 @@ describe("LocalPayment", () => {
paymentTypeCountryCode: "NL",
amount: "10.00",
intent: "sale",
recurrent: undefined,
merchantOrPartnerCustomerId: undefined,
billingAddress: {
line1: undefined,
line2: undefined,
city: undefined,
state: undefined,
postalCode: undefined,
countryCode: undefined,
},
birthDate: undefined,
correlationId: undefined,
discountAmount: undefined,
experienceProfile: {
brandName: "My Brand!",
noShipping: false,
customerServiceInstructions: undefined,
locale: undefined,
},
currencyIsoCode: "USD",
firstName: "First",
lastName: "Last",
payerEmail: "email@example.com",
phone: "1234",
line1: "123 Address",
line2: "Unit 1",
city: "Chicago",
state: "IL",
postalCode: "60654",
countryCode: "US",
lineItems: undefined,
phoneCountryCode: undefined,
shippingAmount: undefined,
merchantAccountId: "merchant-account-id",
bic: "ABGANL6A",
},
});
});
});

it("creates a payment resource for a recurrent payment", () => {
const client = testContext.client;

testContext.options.recurrent = true;
testContext.options.customerId = "12345678";

testContext.frameServiceInstance.open.mockImplementation(
yields(null, { foo: "bar" })
);

return testContext.localPayment
.startPayment(testContext.options)
.then(() => {
expect(client.request).toHaveBeenCalledWith({
method: "post",
endpoint: "local_payments/create",
data: {
cancelUrl: `https://example.com:9292/web/${VERSION}/html/local-payment-redirect-frame.min.html?channel=service-id&r=https%3A%2F%2Fexample.com%2Ffallback&t=Button%20Text&c=1`,
returnUrl: `https://example.com:9292/web/${VERSION}/html/local-payment-redirect-frame.min.html?channel=service-id&r=https%3A%2F%2Fexample.com%2Ffallback&t=Button%20Text`,
fundingSource: "ideal",
paymentTypeCountryCode: "NL",
amount: "10.00",
intent: "sale",
recurrent: true,
merchantOrPartnerCustomerId: "12345678",
billingAddress: {
line1: undefined,
line2: undefined,
Expand Down Expand Up @@ -1028,6 +1111,8 @@ describe("LocalPayment", () => {
paymentTypeCountryCode: "NL",
amount: "10.00",
intent: "sale",
recurrent: undefined,
merchantOrPartnerCustomerId: undefined,
experienceProfile: {
brandName: "My Brand!",
noShipping: false,
Expand Down
76 changes: 0 additions & 76 deletions test/three-d-secure/unit/external/frameworks/inline-iframe.js
Original file line number Diff line number Diff line change
Expand Up @@ -337,81 +337,5 @@ describe("InlineIframeFramework", () => {
}
);
});

/*
* The following two tests pass if and only if there is a
* `.catch()` appended to the rejection of
* `_getDfReferenceIdPromisePlus` within the `setup` method of the
* Songbird framework.
* */
it("passes iframe to merchant for v1 fallback", (done) => {
let framework;

assets.loadScript.mockRejectedValue(new Error("failed"));

framework = new InlineIframeFramework({
createPromise: Promise.resolve(testContext.client),
client: testContext.client,
});

framework.setupSongbird();

framework.on(
"inline-iframe-framework:AUTHENTICATION_IFRAME_AVAILABLE",
(payload, next) => {
expect(
payload.element.querySelector(
'[data-braintree-v1-fallback-iframe-container="true"] iframe'
)
).toBeTruthy();

next();

done();
}
);

framework.initializeChallengeWithLookupResponse(
testContext.lookupResponse,
{
onLookupComplete: yields(),
}
);
});

it("passes iframe to merchant for v1 fallback", (done) => {
let framework;

assets.loadScript.mockRejectedValue(new Error("failed"));

framework = new InlineIframeFramework({
createPromise: Promise.resolve(testContext.client),
client: testContext.client,
});

framework.setupSongbird();

framework.on(
"inline-iframe-framework:AUTHENTICATION_IFRAME_AVAILABLE",
(payload, next) => {
expect(
payload.element.querySelector(
'[data-braintree-v1-fallback-iframe-container="true"] iframe'
)
).toBeDefined();

next();

done();
}
);

framework.initializeChallengeWithLookupResponse(
testContext.lookupResponse,
{
onLookupComplete: yields(),
}
);
});
});
});

0 comments on commit eb5b835

Please sign in to comment.