Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions public/pay/sw-bobbucks.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ const MSG_PING = 'ping';
const MSG_APP_WINDOW_READY = 'payment_app_window_ready';
const MSG_UPDATE_FOR_MERCHANT = 'update_for_merchant';

// Test app types passed via methodData[].data.appType
const DELAY_OPEN_WINDOW = 'delay_open_window';
const RESOLVE_BEFORE_OPEN_WINDOW = 'resolve_before_open_window';
const REJECT_BEFORE_OPEN_WINDOW = 'reject_before_open_window';
const RESOLVE_AFTER_OPEN_WINDOW = 'resolve_after_open_window';
const REJECT_AFTER_OPEN_WINDOW = 'reject_after_open_window';

/**
* A utility class to resolve/reject a Promise from outside its constructor.
* This is useful for bridging event-driven APIs with Promises. In this service
Expand Down Expand Up @@ -46,6 +53,16 @@ PromiseResolver.prototype = {
},
};

function createMockPaymentResponse() {
return {
methodName: 'https://bobbucks.dev/pay',
details: {
bobbucks_token_id: 'MALICIOUS_APP_TEST_TOKEN',
message: 'Test payment completed',
},
};
}

// --- Global state for the current payment flow ---
// We can only handle one payment request at a time.
let currentPayment = {
Expand All @@ -63,6 +80,8 @@ self.addEventListener(PAYMENT_REQUEST_EVENT, async (e) => {
currentPayment.resolver = new PromiseResolver();
e.respondWith(currentPayment.resolver.promise);

const appType = e.methodData?.[0]?.data?.appType;

let url = 'https://bobbucks.dev/pay';
// The methodData here represents what the merchant supports. We could have a
// payment selection screen, but for this simple demo if we see alipay in the list
Expand All @@ -71,6 +90,18 @@ self.addEventListener(PAYMENT_REQUEST_EVENT, async (e) => {
url += '/alipay.html';
}

// Resolve or reject promise before openWindow if specified
if (appType === RESOLVE_BEFORE_OPEN_WINDOW) {
currentPayment.resolver.resolve(createMockPaymentResponse());
} else if (appType === REJECT_BEFORE_OPEN_WINDOW) {
currentPayment.resolver.reject('Payment rejected before openWindow');
}

// Long loading time payment app if specified: delay 5 seconds before openWindow.
if (appType === DELAY_OPEN_WINDOW) {
await new Promise((resolve) => setTimeout(resolve, 5000));
}

try {
const windowClient = await e.openWindow(url);
if (windowClient === null) {
Expand All @@ -79,6 +110,13 @@ self.addEventListener(PAYMENT_REQUEST_EVENT, async (e) => {
} catch (err) {
currentPayment.resolver.reject(err);
}

// Resolve or reject promise immediately after openWindow if specified
if (appType === RESOLVE_AFTER_OPEN_WINDOW) {
currentPayment.resolver.resolve(createMockPaymentResponse());
} else if (appType === REJECT_AFTER_OPEN_WINDOW) {
currentPayment.resolver.reject('Payment rejected immediately after openWindow');
}
});

self.addEventListener(MESSAGE_EVENT, (e) => {
Expand Down