웹 기반 결제 앱을 웹 결제에 맞게 조정하고 고객에게 더 나은 사용자 환경을 제공하는 방법
결제 앱이 등록되면 판매자의 결제 요청을 수락할 수 있습니다. 이 게시물에서는 런타임 중에 (즉, 창이 표시되고 사용자가 이 창과 상호작용할 때) 서비스 워커의 결제 트랜잭션을 조정하는 방법을 설명합니다.
![서비스 워커를 통한 결제 트랜잭션 조정](https://web.developers.google.cn/static/articles/orchestrating-payment-transactions/image/orchestrating-payment-tra-dd17687ea595a.png?authuser=19&hl=ko)
'런타임 결제 매개변수 변경'은 사용자가 결제 핸들러와 상호작용하는 동안 판매자와 결제 핸들러가 메시지를 교환할 수 있도록 하는 이벤트 집합을 의미합니다. 자세한 내용은 서비스 워커로 선택적 결제 정보 처리를 참조하세요.
판매자로부터 결제 요청 이벤트 수신
고객이 개발자의 웹 기반 결제 앱으로 결제하도록 선택하고 판매자가 PaymentRequest.show()
를 호출하면 서비스 워커가 paymentrequest
이벤트를 수신합니다. 서비스 워커에 이벤트 리스너를 추가하여 이벤트를 캡처하고 다음 작업을 준비합니다.
[결제 핸들러] service-worker.js:
…
let payment_request_event;
let resolver;
let client;
// `self` is the global object in service worker
self.addEventListener('paymentrequest', async e => {
if (payment_request_event) {
// If there's an ongoing payment transaction, reject it.
resolver.reject();
}
// Preserve the event for future use
payment_request_event = e;
…
보존된 PaymentRequestEvent
에는 이 트랜잭션에 관한 중요한 정보가 포함되어 있습니다.
속성 이름 | 설명 |
---|---|
topOrigin |
최상위 웹페이지의 출처 (일반적으로 수취인 판매자)를 나타내는 문자열입니다. 판매자 출처를 식별하는 데 사용합니다. |
paymentRequestOrigin |
호출자의 출처를 나타내는 문자열입니다. 이는 판매자가 Payment Request API를 직접 호출하는 경우 topOrigin 과 동일할 수 있지만, API가 서드 파티(예: 결제 게이트웨이)에 의해 iframe 내에서 호출되는 경우에는 다를 수 있습니다.
|
paymentRequestId |
Payment Request API에 제공된 PaymentDetailsInit 의 id 속성입니다. 판매자가 이를 생략하면 브라우저에서 자동 생성된 ID를 제공합니다.
|
methodData |
PaymentMethodData 의 일부로 판매자가 제공하는 결제 수단별 데이터입니다.
결제 거래 세부정보를 확인하는 데 사용합니다.
|
total |
판매자가 PaymentDetailsInit 의 일부로 제공한 총 금액입니다.
이를 사용하여 고객이 결제할 총 금액을 알 수 있는 UI를 구성합니다.
|
instrumentKey |
사용자가 선택한 계측 키입니다. 사전에 제공한 instrumentKey 가 반영됩니다. 빈 문자열은 사용자가 결제 수단을 지정하지 않았음을 나타냅니다.
|
결제 핸들러 창을 열어 웹 기반 결제 앱 프런트엔드를 표시합니다.
paymentrequest
이벤트가 수신되면 결제 앱은 PaymentRequestEvent.openWindow()
를 호출하여 결제 핸들러 창을 열 수 있습니다. 결제 핸들러 창에는 고객이 인증하고 배송지 주소와 옵션을 선택하고 결제를 승인할 수 있는 결제 앱 인터페이스가 표시됩니다. 결제 프런트엔드에서 결제 처리 (제공 예정)에서 프런트엔드 코드를 작성하는 방법을 설명합니다.
향후 결제 결과로 해결할 수 있도록 보존된 프로미스를 PaymentRequestEvent.respondWith()
에 전달합니다.
[결제 핸들러] service-worker.js:
…
self.addEventListener('paymentrequest', async e => {
…
// Retain a promise for future resolution
// Polyfill for PromiseResolver is provided below.
resolver = new PromiseResolver();
// Pass a promise that resolves when payment is done.
e.respondWith(resolver.promise);
// Open the checkout page.
try {
// Open the window and preserve the client
client = await e.openWindow(checkoutURL);
if (!client) {
// Reject if the window fails to open
throw 'Failed to open window';
}
} catch (err) {
// Reject the promise on failure
resolver.reject(err);
};
});
…
편리한 PromiseResolver
폴리필을 사용하여 임의의 시점에 프로미스를 해결할 수 있습니다.
class PromiseResolver {
constructor() {
this.promise_ = new Promise((resolve, reject) => {
this.resolve_ = resolve;
this.reject_ = reject;
})
}
get promise() { return this.promise_ }
get resolve() { return this.resolve_ }
get reject() { return this.reject_ }
}
프런트엔드와 정보 교환
결제 앱의 서비스 워커는 ServiceWorkerController.postMessage()
를 통해 결제 앱의 프런트엔드와 메시지를 교환할 수 있습니다. 프런트엔드에서 메시지를 수신하려면 message
이벤트를 리슨합니다.
[결제 핸들러] service-worker.js:
// Define a convenient `postMessage()` method
const postMessage = (type, contents = {}) => {
if (client) client.postMessage({ type, ...contents });
}
프런트엔드에서 준비 신호 수신
결제 핸들러 창이 열리면 서비스 워커는 결제 앱 프런트엔드의 준비 상태 신호를 기다려야 합니다. 서비스 워커는 프런트엔드가 준비되면 중요한 정보를 프런트엔드에 전달할 수 있습니다.
[결제 핸들러] 프런트엔드:
navigator.serviceWorker.controller.postMessage({
type: 'WINDOW_IS_READY'
});
[결제 핸들러] service-worker.js:
…
// Received a message from the frontend
self.addEventListener('message', async e => {
let details;
try {
switch (e.data.type) {
// `WINDOW_IS_READY` is a frontend's ready state signal
case 'WINDOW_IS_READY':
const { total } = payment_request_event;
…
트랜잭션 세부정보를 프런트엔드에 전달
이제 결제 세부정보를 다시 보내 주세요. 이 경우에는 총 결제 요청 금액만 전송하지만 원하는 경우 더 많은 세부정보를 전달할 수 있습니다.
[결제 핸들러] service-worker.js:
…
// Pass the payment details to the frontend
postMessage('PAYMENT_IS_READY', { total });
break;
…
[결제 핸들러] 프런트엔드:
let total;
navigator.serviceWorker.addEventListener('message', async e => {
switch (e.data.type) {
case 'PAYMENT_IS_READY':
({ total } = e.data);
// Update the UI
renderHTML(total);
break;
…
고객의 결제 사용자 인증 정보 반환
고객이 결제를 승인하면 프런트엔드가 서비스 워커에 게시 메시지를 보내 계속 진행할 수 있습니다. PaymentRequestEvent.respondWith()
에 전달된 프로미스를 해결하여 결과를 다시 판매자에게 보낼 수 있습니다.
PaymentHandlerResponse
객체를 전달합니다.
속성 이름 | 설명 |
---|---|
methodName |
결제에 사용되는 결제 수단 식별자입니다. |
details |
판매자가 결제를 처리하는 데 필요한 정보를 제공하는 결제 수단별 데이터입니다. |
[결제 핸들러] 프런트엔드:
const paymentMethod = …
postMessage('PAYMENT_AUTHORIZED', {
paymentMethod, // Payment method identifier
});
[결제 핸들러] service-worker.js:
…
// Received a message from the frontend
self.addEventListener('message', async e => {
let details;
try {
switch (e.data.type) {
…
case 'PAYMENT_AUTHORIZED':
// Resolve the payment request event promise
// with a payment response object
const response = {
methodName: e.data.paymentMethod,
details: { id: 'put payment credential here' },
}
resolver.resolve(response);
// Don't forget to initialize.
payment_request_event = null;
break;
…
결제 거래 취소
고객이 트랜잭션을 취소할 수 있도록 프런트엔드는 서비스 워커에 게시 메시지를 보내 이를 취소할 수 있습니다. 그러면 서비스 워커는 PaymentRequestEvent.respondWith()
에 전달된 프로미스를 null
로 확인하여 판매자에게 트랜잭션이 취소되었음을 알릴 수 있습니다.
[결제 핸들러] 프런트엔드:
postMessage('CANCEL_PAYMENT');
[결제 핸들러] service-worker.js:
…
// Received a message from the frontend
self.addEventListener('message', async e => {
let details;
try {
switch (e.data.type) {
…
case 'CANCEL_PAYMENT':
// Resolve the payment request event promise
// with null
resolver.resolve(null);
// Don't forget to initialize.
payment_request_event = null;
break;
…
샘플 코드
이 문서에 표시된 모든 샘플 코드는 다음과 같이 작동하는 샘플 앱에서 발췌한 것입니다.
https://paymenthandler-demo.glitch.me
[결제 핸들러] 서비스 워커
[결제 핸들러] 프런트엔드
사용해 보려면 다음 단계를 따르세요.
- https://paymentrequest-demo.glitch.me/로 이동합니다.
- 페이지 하단으로 이동합니다.
- 결제 추가 버튼을 누릅니다.
- 결제 수단 식별자 필드에
https://paymenthandler-demo.glitch.me
를 입력합니다. - 입력란 옆에 있는 결제 버튼을 누릅니다.
다음 단계
이 문서에서는 서비스 워커에서 결제 트랜잭션을 조정하는 방법을 알아보았습니다. 다음 단계는 서비스 워커에 고급 기능을 추가하는 방법을 알아보는 것입니다.