@alphabite/medusa-paypal
Version:
Alphabite's Medusa Paypal Plugin
195 lines • 16.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PaypalService = void 0;
const paypal_server_sdk_1 = require("@paypal/paypal-server-sdk");
const libphonenumber_js_1 = require("libphonenumber-js");
const utils_1 = require("@medusajs/framework/utils");
class PaypalService {
constructor({ clientId, clientSecret, isSandbox, webhookId, includeCustomerData, includeShippingData, }) {
this.verifyWebhook = async ({ headers, body, }) => {
if (!this.webhookId) {
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Webhook ID is not set");
}
const accessToken = await this.getAccessToken();
const verifyWebhookRes = await fetch(`${this.baseUrl}/v1/notifications/verify-webhook-signature`, {
method: "POST",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
auth_algo: headers["paypal-auth-algo"],
cert_url: headers["paypal-cert-url"],
transmission_id: headers["paypal-transmission-id"],
transmission_sig: headers["paypal-transmission-sig"],
transmission_time: headers["paypal-transmission-time"],
webhook_id: this.webhookId,
webhook_event: body,
}),
});
if (!verifyWebhookRes.ok) {
throw new Error(`Failed to verify webhook signature: ${verifyWebhookRes.statusText}`);
}
const verifyWebhookData = await verifyWebhookRes.json();
if (verifyWebhookData.verification_status !== "SUCCESS") {
throw new Error("Failed to verify webhook signature");
}
return { status: verifyWebhookData.verification_status, body };
};
const environment = isSandbox ? paypal_server_sdk_1.Environment.Sandbox : paypal_server_sdk_1.Environment.Production;
this.client = new paypal_server_sdk_1.Client({
clientCredentialsAuthCredentials: {
oAuthClientId: clientId,
oAuthClientSecret: clientSecret,
},
timeout: 0,
environment,
logging: {
logLevel: paypal_server_sdk_1.LogLevel.Info,
logRequest: {
logBody: true,
},
logResponse: {
logHeaders: true,
},
},
});
this.baseUrl = isSandbox ? "https://api-m.sandbox.paypal.com" : "https://api-m.paypal.com";
this.clientId = clientId;
this.clientSecret = clientSecret;
this.webhookId = webhookId;
this.ordersController = new paypal_server_sdk_1.OrdersController(this.client);
this.paymentsController = new paypal_server_sdk_1.PaymentsController(this.client);
this.authController = new paypal_server_sdk_1.OAuthAuthorizationController(this.client);
this.includeCustomerData = !!includeCustomerData;
this.includeShippingData = !!includeShippingData;
}
async getAccessToken() {
try {
const authorization = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString("base64");
const authRes = await this.authController.requestToken({
authorization: `Basic ${authorization}`,
});
const accessToken = authRes.result.accessToken;
if (!accessToken)
throw new Error("Failed to get access token");
return accessToken;
}
catch (error) {
throw new Error("Failed to get access token: " + error.message);
}
}
async createOrder({ amount, currency, sessionId, shipping_info, items, email, }) {
const ordersController = new paypal_server_sdk_1.OrdersController(this.client);
const paypalItems = items?.map((item) => ({
name: item.title,
quantity: item.quantity.toString(),
unitAmount: {
currencyCode: currency,
value: item.unit_price.toString(),
},
})) || [];
const hasItems = paypalItems.length > 0;
const shippingData = !!shipping_info && {
...(this.includeCustomerData && this.mapCustomerData({ email, shipping_info })),
...(this.includeShippingData && this.mapShippingData(shipping_info)),
type: paypal_server_sdk_1.FulfillmentType.Shipping,
};
const createdOrder = await ordersController.createOrder({
body: {
intent: paypal_server_sdk_1.CheckoutPaymentIntent.Capture,
purchaseUnits: [
{
amount: {
currencyCode: currency,
value: amount.toString(),
...(hasItems && {
breakdown: {
itemTotal: {
currencyCode: currency,
value: amount.toString(),
},
},
}),
},
customId: sessionId,
...(hasItems && { items: paypalItems }),
...(shippingData && { shipping: shippingData }),
},
],
applicationContext: {
...(this.includeShippingData &&
shippingData && {
shippingPreference: paypal_server_sdk_1.OrderApplicationContextShippingPreference.SetProvidedAddress,
}),
userAction: paypal_server_sdk_1.OrderApplicationContextUserAction.PayNow,
},
},
});
if (!createdOrder?.result?.id)
throw new Error("Failed to create order");
return createdOrder.result;
}
async captureOrder(id) {
const capturedOrder = await this.ordersController.captureOrder({
id,
});
return capturedOrder.result;
}
async retrieveOrder(id) {
const orderDetails = await this.ordersController.getOrder({
id,
});
return orderDetails.result;
}
async authorizeOrder(id) {
const authorizedOrder = await this.ordersController.authorizeOrder({
id,
});
return authorizedOrder.result;
}
async refundPayment(captureIds) {
const refunds = [];
for (const captureId of captureIds) {
const refund = await this.paymentsController.refundCapturedPayment({
captureId,
});
refunds.push(refund.result);
}
return refunds;
}
mapCustomerData({ email, shipping_info, }) {
if (!this.includeCustomerData || !shipping_info) {
return undefined;
}
const parsedPhoneNumber = !!shipping_info?.phone && (0, libphonenumber_js_1.parsePhoneNumberFromString)(shipping_info.phone);
return {
name: {
fullName: `${shipping_info.first_name} ${shipping_info.last_name}`,
},
...(email && { emailAddress: email }),
...(parsedPhoneNumber && {
phoneNumber: {
countryCode: parsedPhoneNumber.countryCallingCode,
nationalNumber: parsedPhoneNumber.nationalNumber,
},
}),
};
}
mapShippingData(shipping_info) {
if (!this.includeShippingData || !shipping_info || !shipping_info.country_code) {
return undefined;
}
return {
address: {
countryCode: shipping_info.country_code,
postalCode: shipping_info.postal_code,
adminArea1: shipping_info.province,
adminArea2: shipping_info.city,
addressLine1: shipping_info.address_1,
},
};
}
}
exports.PaypalService = PaypalService;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGF5cGFsLWNvcmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvcHJvdmlkZXJzL3BheXBhbC9wYXlwYWwtY29yZS9wYXlwYWwtY29yZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxpRUFnQm1DO0FBRW5DLHlEQUErRDtBQUUvRCxxREFBd0Q7QUFXeEQsTUFBYSxhQUFhO0lBWXhCLFlBQVksRUFDVixRQUFRLEVBQ1IsWUFBWSxFQUNaLFNBQVMsRUFDVCxTQUFTLEVBQ1QsbUJBQW1CLEVBQ25CLG1CQUFtQixHQUNVO1FBNEp4QixrQkFBYSxHQUFHLEtBQUssRUFBRSxFQUM1QixPQUFPLEVBQ1AsSUFBSSxHQUlMLEVBQTRELEVBQUU7WUFDN0QsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztnQkFDcEIsTUFBTSxJQUFJLG1CQUFXLENBQUMsbUJBQVcsQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUFFLHVCQUF1QixDQUFDLENBQUM7WUFDakYsQ0FBQztZQUVELE1BQU0sV0FBVyxHQUFHLE1BQU0sSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBRWhELE1BQU0sZ0JBQWdCLEdBQUcsTUFBTSxLQUFLLENBQUMsR0FBRyxJQUFJLENBQUMsT0FBTyw0Q0FBNEMsRUFBRTtnQkFDaEcsTUFBTSxFQUFFLE1BQU07Z0JBQ2QsT0FBTyxFQUFFO29CQUNQLGFBQWEsRUFBRSxVQUFVLFdBQVcsRUFBRTtvQkFDdEMsY0FBYyxFQUFFLGtCQUFrQjtpQkFDbkM7Z0JBQ0QsSUFBSSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUM7b0JBQ25CLFNBQVMsRUFBRSxPQUFPLENBQUMsa0JBQWtCLENBQUM7b0JBQ3RDLFFBQVEsRUFBRSxPQUFPLENBQUMsaUJBQWlCLENBQUM7b0JBQ3BDLGVBQWUsRUFBRSxPQUFPLENBQUMsd0JBQXdCLENBQUM7b0JBQ2xELGdCQUFnQixFQUFFLE9BQU8sQ0FBQyx5QkFBeUIsQ0FBQztvQkFDcEQsaUJBQWlCLEVBQUUsT0FBTyxDQUFDLDBCQUEwQixDQUFDO29CQUN0RCxVQUFVLEVBQUUsSUFBSSxDQUFDLFNBQVM7b0JBQzFCLGFBQWEsRUFBRSxJQUFJO2lCQUNwQixDQUFDO2FBQ0gsQ0FBQyxDQUFDO1lBRUgsSUFBSSxDQUFDLGdCQUFnQixDQUFDLEVBQUUsRUFBRSxDQUFDO2dCQUN6QixNQUFNLElBQUksS0FBSyxDQUFDLHVDQUF1QyxnQkFBZ0IsQ0FBQyxVQUFVLEVBQUUsQ0FBQyxDQUFDO1lBQ3hGLENBQUM7WUFFRCxNQUFNLGlCQUFpQixHQUFHLE1BQU0sZ0JBQWdCLENBQUMsSUFBSSxFQUFFLENBQUM7WUFFeEQsSUFBSSxpQkFBaUIsQ0FBQyxtQkFBbUIsS0FBSyxTQUFTLEVBQUUsQ0FBQztnQkFDeEQsTUFBTSxJQUFJLEtBQUssQ0FBQyxvQ0FBb0MsQ0FBQyxDQUFDO1lBQ3hELENBQUM7WUFFRCxPQUFPLEVBQUUsTUFBTSxFQUFFLGlCQUFpQixDQUFDLG1CQUFtQixFQUFFLElBQUksRUFBRSxDQUFDO1FBQ2pFLENBQUMsQ0FBQztRQXBNQSxNQUFNLFdBQVcsR0FBRyxTQUFTLENBQUMsQ0FBQyxDQUFDLCtCQUFXLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQywrQkFBVyxDQUFDLFVBQVUsQ0FBQztRQUU3RSxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksMEJBQU0sQ0FBQztZQUN2QixnQ0FBZ0MsRUFBRTtnQkFDaEMsYUFBYSxFQUFFLFFBQVE7Z0JBQ3ZCLGlCQUFpQixFQUFFLFlBQVk7YUFDaEM7WUFDRCxPQUFPLEVBQUUsQ0FBQztZQUNWLFdBQVc7WUFDWCxPQUFPLEVBQUU7Z0JBQ1AsUUFBUSxFQUFFLDRCQUFRLENBQUMsSUFBSTtnQkFDdkIsVUFBVSxFQUFFO29CQUNWLE9BQU8sRUFBRSxJQUFJO2lCQUNkO2dCQUNELFdBQVcsRUFBRTtvQkFDWCxVQUFVLEVBQUUsSUFBSTtpQkFDakI7YUFDRjtTQUNGLENBQUMsQ0FBQztRQUVILElBQUksQ0FBQyxPQUFPLEdBQUcsU0FBUyxDQUFDLENBQUMsQ0FBQyxrQ0FBa0MsQ0FBQyxDQUFDLENBQUMsMEJBQTBCLENBQUM7UUFFM0YsSUFBSSxDQUFDLFFBQVEsR0FBRyxRQUFRLENBQUM7UUFDekIsSUFBSSxDQUFDLFlBQVksR0FBRyxZQUFZLENBQUM7UUFDakMsSUFBSSxDQUFDLFNBQVMsR0FBRyxTQUFTLENBQUM7UUFFM0IsSUFBSSxDQUFDLGdCQUFnQixHQUFHLElBQUksb0NBQWdCLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQzFELElBQUksQ0FBQyxrQkFBa0IsR0FBRyxJQUFJLHNDQUFrQixDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUM5RCxJQUFJLENBQUMsY0FBYyxHQUFHLElBQUksZ0RBQTRCLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBRXBFLElBQUksQ0FBQyxtQkFBbUIsR0FBRyxDQUFDLENBQUMsbUJBQW1CLENBQUM7UUFDakQsSUFBSSxDQUFDLG1CQUFtQixHQUFHLENBQUMsQ0FBQyxtQkFBbUIsQ0FBQztJQUNuRCxDQUFDO0lBRUQsS0FBSyxDQUFDLGNBQWM7UUFDbEIsSUFBSSxDQUFDO1lBQ0gsTUFBTSxhQUFhLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLElBQUksQ0FBQyxRQUFRLElBQUksSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1lBRTlGLE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDLGNBQWMsQ0FBQyxZQUFZLENBQUM7Z0JBQ3JELGFBQWEsRUFBRSxTQUFTLGFBQWEsRUFBRTthQUN4QyxDQUFDLENBQUM7WUFFSCxNQUFNLFdBQVcsR0FBRyxPQUFPLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQztZQUUvQyxJQUFJLENBQUMsV0FBVztnQkFBRSxNQUFNLElBQUksS0FBSyxDQUFDLDRCQUE0QixDQUFDLENBQUM7WUFFaEUsT0FBTyxXQUFXLENBQUM7UUFDckIsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixNQUFNLElBQUksS0FBSyxDQUFDLDhCQUE4QixHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNsRSxDQUFDO0lBQ0gsQ0FBQztJQUVELEtBQUssQ0FBQyxXQUFXLENBQUMsRUFDaEIsTUFBTSxFQUNOLFFBQVEsRUFDUixTQUFTLEVBQ1QsYUFBYSxFQUNiLEtBQUssRUFDTCxLQUFLLEdBQ2tCO1FBQ3ZCLE1BQU0sZ0JBQWdCLEdBQUcsSUFBSSxvQ0FBZ0IsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7UUFFM0QsTUFBTSxXQUFXLEdBQ2YsS0FBSyxFQUFFLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsQ0FBQztZQUNwQixJQUFJLEVBQUUsSUFBSSxDQUFDLEtBQUs7WUFDaEIsUUFBUSxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxFQUFFO1lBQ2xDLFVBQVUsRUFBRTtnQkFDVixZQUFZLEVBQUUsUUFBUTtnQkFDdEIsS0FBSyxFQUFFLElBQUksQ0FBQyxVQUFVLENBQUMsUUFBUSxFQUFFO2FBQ2xDO1NBQ0YsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO1FBRVosTUFBTSxRQUFRLEdBQUcsV0FBVyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUM7UUFFeEMsTUFBTSxZQUFZLEdBQTRCLENBQUMsQ0FBQyxhQUFhLElBQUk7WUFDL0QsR0FBRyxDQUFDLElBQUksQ0FBQyxtQkFBbUIsSUFBSSxJQUFJLENBQUMsZUFBZSxDQUFDLEVBQUUsS0FBSyxFQUFFLGFBQWEsRUFBRSxDQUFDLENBQUM7WUFDL0UsR0FBRyxDQUFDLElBQUksQ0FBQyxtQkFBbUIsSUFBSSxJQUFJLENBQUMsZUFBZSxDQUFDLGFBQWEsQ0FBQyxDQUFDO1lBQ3BFLElBQUksRUFBRSxtQ0FBZSxDQUFDLFFBQVE7U0FDL0IsQ0FBQztRQUVGLE1BQU0sWUFBWSxHQUFHLE1BQU0sZ0JBQWdCLENBQUMsV0FBVyxDQUFDO1lBQ3RELElBQUksRUFBRTtnQkFDSixNQUFNLEVBQUUseUNBQXFCLENBQUMsT0FBTztnQkFDckMsYUFBYSxFQUFFO29CQUNiO3dCQUNFLE1BQU0sRUFBRTs0QkFDTixZQUFZLEVBQUUsUUFBUTs0QkFDdEIsS0FBSyxFQUFFLE1BQU0sQ0FBQyxRQUFRLEVBQUU7NEJBQ3hCLEdBQUcsQ0FBQyxRQUFRLElBQUk7Z0NBQ2QsU0FBUyxFQUFFO29DQUNULFNBQVMsRUFBRTt3Q0FDVCxZQUFZLEVBQUUsUUFBUTt3Q0FDdEIsS0FBSyxFQUFFLE1BQU0sQ0FBQyxRQUFRLEVBQUU7cUNBQ3pCO2lDQUNGOzZCQUNGLENBQUM7eUJBQ0g7d0JBQ0QsUUFBUSxFQUFFLFNBQVM7d0JBQ25CLEdBQUcsQ0FBQyxRQUFRLElBQUksRUFBRSxLQUFLLEVBQUUsV0FBVyxFQUFFLENBQUM7d0JBQ3ZDLEdBQUcsQ0FBQyxZQUFZLElBQUksRUFBRSxRQUFRLEVBQUUsWUFBWSxFQUFFLENBQUM7cUJBQ2hEO2lCQUNGO2dCQUNELGtCQUFrQixFQUFFO29CQUNsQixHQUFHLENBQUMsSUFBSSxDQUFDLG1CQUFtQjt3QkFDMUIsWUFBWSxJQUFJO3dCQUNkLGtCQUFrQixFQUFFLDZEQUF5QyxDQUFDLGtCQUFrQjtxQkFDakYsQ0FBQztvQkFDSixVQUFVLEVBQUUscURBQWlDLENBQUMsTUFBTTtpQkFDckQ7YUFDRjtTQUNGLENBQUMsQ0FBQztRQUVILElBQUksQ0FBQyxZQUFZLEVBQUUsTUFBTSxFQUFFLEVBQUU7WUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLHdCQUF3QixDQUFDLENBQUM7UUFFekUsT0FBTyxZQUFZLENBQUMsTUFBTSxDQUFDO0lBQzdCLENBQUM7SUFFRCxLQUFLLENBQUMsWUFBWSxDQUFDLEVBQVU7UUFDM0IsTUFBTSxhQUFhLEdBQUcsTUFBTSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsWUFBWSxDQUFDO1lBQzdELEVBQUU7U0FDSCxDQUFDLENBQUM7UUFFSCxPQUFPLGFBQWEsQ0FBQyxNQUFNLENBQUM7SUFDOUIsQ0FBQztJQUVELEtBQUssQ0FBQyxhQUFhLENBQUMsRUFBVTtRQUM1QixNQUFNLFlBQVksR0FBRyxNQUFNLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxRQUFRLENBQUM7WUFDeEQsRUFBRTtTQUNILENBQUMsQ0FBQztRQUVILE9BQU8sWUFBWSxDQUFDLE1BQU0sQ0FBQztJQUM3QixDQUFDO0lBRUQsS0FBSyxDQUFDLGNBQWMsQ0FBQyxFQUFVO1FBQzdCLE1BQU0sZUFBZSxHQUFHLE1BQU0sSUFBSSxDQUFDLGdCQUFnQixDQUFDLGNBQWMsQ0FBQztZQUNqRSxFQUFFO1NBQ0gsQ0FBQyxDQUFDO1FBRUgsT0FBTyxlQUFlLENBQUMsTUFBTSxDQUFDO0lBQ2hDLENBQUM7SUFFRCxLQUFLLENBQUMsYUFBYSxDQUFDLFVBQW9CO1FBQ3RDLE1BQU0sT0FBTyxHQUFhLEVBQUUsQ0FBQztRQUU3QixLQUFLLE1BQU0sU0FBUyxJQUFJLFVBQVUsRUFBRSxDQUFDO1lBQ25DLE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLGtCQUFrQixDQUFDLHFCQUFxQixDQUFDO2dCQUNqRSxTQUFTO2FBQ1YsQ0FBQyxDQUFDO1lBRUgsT0FBTyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDOUIsQ0FBQztRQUVELE9BQU8sT0FBTyxDQUFDO0lBQ2pCLENBQUM7SUE2Q08sZUFBZSxDQUFDLEVBQ3RCLEtBQUssRUFDTCxhQUFhLEdBSWQ7UUFDQyxJQUFJLENBQUMsSUFBSSxDQUFDLG1CQUFtQixJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7WUFDaEQsT0FBTyxTQUFTLENBQUM7UUFDbkIsQ0FBQztRQUVELE1BQU0saUJBQWlCLEdBQUcsQ0FBQyxDQUFDLGFBQWEsRUFBRSxLQUFLLElBQUksSUFBQSw4Q0FBMEIsRUFBQyxhQUFhLENBQUMsS0FBSyxDQUFDLENBQUM7UUFFcEcsT0FBTztZQUNMLElBQUksRUFBRTtnQkFDSixRQUFRLEVBQUUsR0FBRyxhQUFhLENBQUMsVUFBVSxJQUFJLGFBQWEsQ0FBQyxTQUFTLEVBQUU7YUFDbkU7WUFDRCxHQUFHLENBQUMsS0FBSyxJQUFJLEVBQUUsWUFBWSxFQUFFLEtBQUssRUFBRSxDQUFDO1lBQ3JDLEdBQUcsQ0FBQyxpQkFBaUIsSUFBSTtnQkFDdkIsV0FBVyxFQUFFO29CQUNYLFdBQVcsRUFBRSxpQkFBaUIsQ0FBQyxrQkFBa0I7b0JBQ2pELGNBQWMsRUFBRSxpQkFBaUIsQ0FBQyxjQUFjO2lCQUNqRDthQUNGLENBQUM7U0FDSCxDQUFDO0lBQ0osQ0FBQztJQUVPLGVBQWUsQ0FDckIsYUFBc0Q7UUFFdEQsSUFBSSxDQUFDLElBQUksQ0FBQyxtQkFBbUIsSUFBSSxDQUFDLGFBQWEsSUFBSSxDQUFDLGFBQWEsQ0FBQyxZQUFZLEVBQUUsQ0FBQztZQUMvRSxPQUFPLFNBQVMsQ0FBQztRQUNuQixDQUFDO1FBRUQsT0FBTztZQUNMLE9BQU8sRUFBRTtnQkFDUCxXQUFXLEVBQUUsYUFBYSxDQUFDLFlBQVk7Z0JBQ3ZDLFVBQVUsRUFBRSxhQUFhLENBQUMsV0FBVztnQkFDckMsVUFBVSxFQUFFLGFBQWEsQ0FBQyxRQUFRO2dCQUNsQyxVQUFVLEVBQUUsYUFBYSxDQUFDLElBQUk7Z0JBQzlCLFlBQVksRUFBRSxhQUFhLENBQUMsU0FBUzthQUN0QztTQUNGLENBQUM7SUFDSixDQUFDO0NBQ0Y7QUF0UUQsc0NBc1FDIn0=