@liberquack/utils
Version:
Quack utilities
107 lines • 4.36 kB
JavaScript
import { inlineErr } from "../inline-error.js";
/**
* PaymentClient is the class for executing the checkout on front-end
*/
export class AbstractPaymentClient {
inited = false;
providers = [];
providersInitializers;
constructor(providers) {
this.providersInitializers = providers;
}
init() {
if (this.inited)
return;
this.providers = this.providersInitializers.map(it => it());
this.inited = true;
}
async buildUi(calculatedCheckout, opts) {
this.checkInited();
const provider = this.getProvider(calculatedCheckout);
const checkoutContainer = document.createElement("div");
checkoutContainer.setAttribute("style", "position: relative");
const overlay = document.createElement("div");
overlay.setAttribute("style", `
position: absolute;
display: grid;
align-items: center;
justify-items: center;
left: 0;
right: 0;
top: 0;
bottom: 0;
opacity: 0;
transition: .3s;
pointer-events: none;
`);
const overlayLoading = overlay.cloneNode();
overlayLoading.style.background = "#0008";
const loadingIndicator = document.createElement("div");
loadingIndicator.textContent = "Processing...";
overlayLoading.append(loadingIndicator);
const overlaySuccess = overlay.cloneNode();
overlaySuccess.style.background = "#fff";
const successIndicator = document.createElement("div");
successIndicator.textContent = "Success!";
overlaySuccess.append(successIndicator);
const providerElm = await provider.buildUi(calculatedCheckout, opts, async (calculatedCheckout) => {
overlayLoading.style.opacity = "1";
overlayLoading.style.pointerEvents = "";
const [checkoutExecution, err] = await inlineErr(this.checkout(calculatedCheckout));
if (checkoutExecution?.success) {
opts.onSuccess(checkoutExecution);
overlaySuccess.style.opacity = "1";
overlaySuccess.style.pointerEvents = "";
}
else {
overlayLoading.style.opacity = "0";
overlayLoading.style.pointerEvents = "none";
const errorMessage = checkoutExecution?.errorMessage || err?.message || err;
opts.onError(errorMessage || "Unexpected error", checkoutExecution);
}
});
checkoutContainer.append(providerElm);
checkoutContainer.append(overlayLoading);
checkoutContainer.append(overlaySuccess);
return checkoutContainer;
}
async calculateCheckout(checkout) {
const calculatedCheckout = this.sendCalculateCheckout(checkout);
return calculatedCheckout;
}
async checkout(checkout) {
this.checkInited();
const providerInstance = this.getProvider(checkout);
let currentRoundTrip = 0;
let maxRoundTrips = providerInstance.maxRoundTrips() ?? 0;
let lastExecution;
while (currentRoundTrip < maxRoundTrips) {
const providerCheckout = await providerInstance.checkout(lastExecution ?? checkout);
lastExecution = await this.sendCheckout(providerCheckout);
if (lastExecution?.success) {
return lastExecution;
}
if (lastExecution?.errorMessage) {
break;
}
currentRoundTrip++;
}
throw lastExecution?.errorMessage ?? "Payment failed, try other payment method\nor contact support";
}
getProvider(checkout) {
const providerInstance = this.providers.find(it => it.provider === checkout.provider);
if (!providerInstance) {
throw `There's no provider ${checkout.provider} available, try one of [${this.providers.map(it => it.provider)}]`;
}
return providerInstance;
}
checkInited() {
if (!this.inited)
throw "Payment client instance has still not been initiated";
}
async cancelCheckout(checkout, reason) {
const nextCheckout = { ...checkout, reason };
return this.sendCancelCheckout(checkout);
}
}
//# sourceMappingURL=abstract-payment-client.js.map