Skip to content

Commit

Permalink
WIP: Merge OXID7 with OXID6.3
Browse files Browse the repository at this point in the history
  • Loading branch information
mariolorenz committed Dec 20, 2024
1 parent 3b085f0 commit 9bd61f9
Show file tree
Hide file tree
Showing 35 changed files with 748 additions and 134 deletions.
1 change: 1 addition & 0 deletions out/src/js/paypal-frontend.min.js

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

64 changes: 64 additions & 0 deletions resources/build/js/paypal-frontend-googlepay-3ds.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// After receiving PAYER_ACTION_REQUIRED from PayPal

window.OxidPayPalGooglePay3DS = {
handle: async (paypalOrderResponse) => {
// Create new payment data request with 3DS parameters
const paymentDataRequest = await window.OxidPayPalGooglePay.getGooglePaymentDataRequest();
const threeDSPaymentDataRequest = {
...paymentDataRequest,
paymentData: {
tokenizationData: {
type: 'PAYMENT_GATEWAY',
token: paypalOrderResponse.id // Original PayPal order ID
},
// 3DS specific parameters
threeDSData: {
authenticationParameters: {
threeDSRequestData: {
threeDSServerTransID: generateTransactionId(),
challengeWindowSize: "03", // Full screen
messageCategory: "PAYMENT_AUTHENTICATION"
}
}
}
}
};

try {
// This triggers the 3DS popup
const paymentData = await paymentClient.loadPaymentData(threeDSPaymentDataRequest);

// After successful 3DS authentication
// Update PayPal order with new payment token
const updateOrderResponse = await fetch(`/v2/checkout/orders/${paypalOrderResponse.id}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify([{
op: 'replace',
path: '/payment_source/google_pay/card/authentication_result',
value: {
liability_shift: "POSSIBLE",
three_d_secure: {
authentication_status: "Y",
enrollment_status: "Y"
}
}
}])
});

// Finally capture the order
if (updateOrderResponse.ok) {
const captureResponse = await fetch(`/v2/checkout/orders/${paypalOrderResponse.id}/capture`, {
method: 'POST'
});
return captureResponse;
}
} catch (error) {
// Handle 3DS or payment errors
console.error('3DS Authentication failed:', error);
throw error;
}
}
};
228 changes: 228 additions & 0 deletions resources/build/js/paypal-frontend-googlepay.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
window.OxidPayPalGooglePay = {

baseRequest: {
apiVersion: 2,
apiVersionMinor: 0,
},
paymentsClient: null,
allowedPaymentMethods: null,
merchantInfo: null,
googlePayContainer: null,
buttonId: null,
token: null,
selfLink: null,
useGooglePayAddress: null,
isSandbox: null,
merchantName: null,
totalPrice: null,
currency: null,
deliveryAddressMD5: null,
language: null,
loadingContainer: null,
init: async function () {
this.googlePayContainer = document.getElementById('oscpaypal_googlepay');
if (this.googlePayContainer) {
this.buttonId = this.googlePayContainer.dataset.buttonId;
this.token = this.googlePayContainer.dataset.token;
this.selfLink = this.googlePayContainer.dataset.selfLink;
this.useGooglePayAddress = !!Number(this.googlePayContainer.dataset.useGooglePayAddress);
this.isSandbox = !!Number(this.googlePayContainer.dataset.isSandbox);
this.merchantName = this.googlePayContainer.dataset.merchantName;
this.totalPrice = this.googlePayContainer.dataset.totalPrice;
this.currency = this.googlePayContainer.dataset.currency;
this.deliveryAddressMD5 = this.googlePayContainer.dataset.deliveryAddressMd5;
this.language = this.googlePayContainer.dataset.language;
let elements = document.getElementsByClassName(this.googlePayContainer.dataset.loadingContainerClassName);
this.loadingContainer = elements[0];

await window.googlePayReady;
this.onGooglePayLoaded();
}
},
getGoogleIsReadyToPayRequest: function (allowedPaymentMethods) {
return Object.assign({}, this.baseRequest, {
allowedPaymentMethods: allowedPaymentMethods
});
},

/* Fetch Default Config from PayPal via PayPal SDK */
getGooglePayConfig: async function () {
if (this.allowedPaymentMethods == null || this.merchantInfo == null) {
const googlePayConfig = await paypal.Googlepay().config();
this.allowedPaymentMethods = googlePayConfig.allowedPaymentMethods;
this.merchantInfo = googlePayConfig.merchantInfo;
this.merchantInfo.merchantName = this.merchantName;
}
return {
allowedPaymentMethods: this.allowedPaymentMethods,
merchantInfo: this.merchantInfo
};
},

/* Configure support for the Google Pay API */
getGooglePaymentDataRequest: async function () {
const paymentDataRequest = Object.assign({}, this.baseRequest);
const {allowedPaymentMethods, merchantInfo} = await this.getGooglePayConfig();

paymentDataRequest.transactionInfo = this.getGoogleTransactionInfo();
paymentDataRequest.allowedPaymentMethods = allowedPaymentMethods;
paymentDataRequest.merchantInfo = merchantInfo;
paymentDataRequest.callbackIntents = ["PAYMENT_AUTHORIZATION"];
paymentDataRequest.emailRequired = true;
paymentDataRequest.shippingAddressRequired = this.useGooglePayAddress;
paymentDataRequest.shippingAddressParameters = {'phoneNumberRequired': true};

return paymentDataRequest;
},

onPaymentAuthorized: function (paymentData) {
return new Promise(function (resolve) {
this.processPayment(paymentData)
.then(function () {
resolve({transactionState: "SUCCESS"});
})
.catch(function () {
resolve({transactionState: "ERROR"});
});
}.bind(this));
},

getGooglePaymentsClient: function () {
if (this.paymentsClient === null) {
this.paymentsClient = new google.payments.api.PaymentsClient({
environment: this.isSandbox ? "TEST" : "PRODUCTION",
paymentDataCallbacks: {
onPaymentAuthorized: this.onPaymentAuthorized.bind(this),
},
});
}
return this.paymentsClient;
},
onGooglePayLoaded: async function () {
if (window.OxidPayPal && window.OxidPayPal.isSDKLoaded()) {
const paymentsClient = this.getGooglePaymentsClient();
const {allowedPaymentMethods} = await this.getGooglePayConfig();
paymentsClient
.isReadyToPay(this.getGoogleIsReadyToPayRequest(allowedPaymentMethods))
.then(function (response) {
if (response.result) {
this.loadingContainer.style.display = 'none';
this.addGooglePayButton();
}
}.bind(this))
.catch(function (err) {
console.error(err);
});
} else {
window.setTimeout(this.onGooglePayLoaded.bind(this), 500);
}
},

addGooglePayButton: function () {
const paymentsClient = this.getGooglePaymentsClient();
const button = paymentsClient.createButton({
buttonType: 'buy',
buttonLocale: this.language,
onClick: this.onGooglePaymentButtonClicked.bind(this),
});
document.getElementById("oscpaypal_googlepay").appendChild(button);
},

getGoogleTransactionInfo: function () {
return {
currencyCode: this.currency,
totalPriceStatus: "FINAL",
totalPrice: this.totalPrice,
totalPriceLabel: "Total",
};
},

onGooglePaymentButtonClicked: async function () {
const paymentDataRequest = await this.getGooglePaymentDataRequest();
paymentDataRequest.transactionInfo = this.getGoogleTransactionInfo();
const paymentsClient = this.getGooglePaymentsClient();
paymentsClient.loadPaymentData(paymentDataRequest);
},

processPayment: async function (paymentData) {
try {
const createOrderUrl = this.selfLink + '&cl=oscpaypalproxy&fnc=createGooglepayOrder&paymentid=oscpaypal_googlepay&context=continue&stoken=' + this.token;

const {id: orderId, status, links} = await fetch(createOrderUrl, {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify(paymentData),
}).then((res) => res.json());

const hateoasLinks = new OxidPayPalHateoasLinks();
const approveLink = hateoasLinks.getApproveLink(links);

if (approveLink) {
window.location.href = approveLink;
} else if (status === "APPROVED") {
/* Capture the Order */
this.captureOrder(orderId);
return {transactionState: "SUCCESS"};
} else if (status === "PAYER_ACTION_REQUIRED") {
console.log("==== Confirm Payment Completed Payer Action Required =====");
this.googlePayUserActionRequired(orderId);
} else {
console.error("Payment was not approved");
return {transactionState: "ERROR"};
}
} catch (err) {
return {
transactionState: "ERROR",
error: {
message: err.message,
},
};
}
},
googlePayUserActionRequired: function (orderId) {
paypal
.Googlepay()
.initiatePayerAction({ orderId: orderId })
.then(async () => {
console.log("===== Payer Action Completed =====");
await this.createOxidOrder(orderId);
});
},
createOxidOrder: async function (orderId) {
const url = this.selfLink + '&cl=order&fnc=createGooglePayOrder&context=continue&stoken=' + this.token + '&sDeliveryAddressMD5=' + this.deliveryAddressMD5;
createData = new FormData();
createData.append('orderID', orderId);
fetch(url, {
method: 'POST',
body: createData
}).then(function (res) {
return res.json();
}).then(function (data) {
if (data.status === "ERROR") {
location.reload();
}
});
},
captureOrder: async function (orderId) {
captureData = new FormData();
captureData.append('orderID', orderId);
await fetch(this.selfLink + '&cl=order&fnc=captureGooglePayOrder&context=continue&stoken=' + this.token + '&sDeliveryAddressMD5=' + this.deliveryAddressMD5, {
method: 'post',
body: captureData
}).then(function (res) {
return res.json();
}).then(function (data) {
console.log("==== Capture Order Completed ====");
var goNext = Array.isArray(data.location) && data.location[0];

window.location.href = this.selfLink + goNext;
if (data.status === "ERROR") {
location.reload();
}
}.bind(this)).catch(reason => {
console.error(reason);
});
}
};

OxidPayPalGooglePay.init();
29 changes: 29 additions & 0 deletions resources/build/js/paypal-frontend-hateoaslinks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
(function () {
'use strict';

function OxidPayPalHateoasLinks() {
}
/**
* @typedef {Object} Link
* @property {string} rel - The relation type.
* @property {string} href - The link URL.
*/

/**
* Process an array of links.
* @param {Link[]} links - An array of link objects.
*
* @returns {string|null}
*/
OxidPayPalHateoasLinks.prototype.getApproveLink = function (links) {
const approveHateoasLink = links.find(link => link.rel === 'approve');

if (approveHateoasLink) {
return approveHateoasLink.href;
}

return null;
};

window.OxidPayPalHateoasLinks = OxidPayPalHateoasLinks;
})();
9 changes: 9 additions & 0 deletions resources/build/js/paypal-frontend-paypal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
window.OxidPayPal = {
sdkLoaded: false,
onSDKLoaded: function () {
this.sdkLoaded = true;
},
isSDKLoaded: function () {
return this.sdkLoaded;
}
};
6 changes: 6 additions & 0 deletions resources/grunt/concat.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ module.exports = {
"node_modules/jquery/dist/jquery.js",
"node_modules/popper.js/dist/umd/popper.js",
"node_modules/bootstrap/dist/js/bootstrap.js"
],
"../out/src/js/paypal-frontend.min.js": [
"build/js/paypal-frontend-paypal.js",
"build/js/paypal-frontend-googlepay.js",
"build/js/paypal-frontend-googlepay-3ds.js",
"build/js/paypal-frontend-hateoaslinks.js",
]
}
}
Expand Down
3 changes: 2 additions & 1 deletion resources/grunt/jshint.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ module.exports = {
browser: true,
globals: {
jQuery: true
}
},
esversion: 9
},
moduleproduction: {
src: [
Expand Down
Loading

0 comments on commit 9bd61f9

Please sign in to comment.