결제 거래의 처리 과정

판매자가 결제 앱을 통합하는 방법과 결제 거래가 Payment Request API와 함께 작동하는 방식을 알아봅니다.

밀리카 미하즐리야
밀리카 미하즐리야

Web Payments API는 브라우저에 최초로 내장된 전용 결제 기능입니다. 웹 결제를 사용하면 판매자와 결제 앱의 통합이 더 간단해지고 고객 환경이 간소화되고 더 안전해집니다.

웹 결제 사용의 이점에 관한 자세한 내용은 웹 결제로 결제 앱 지원을 참고하세요.

이 도움말에서는 판매자 웹사이트에서의 결제 거래를 안내하고 결제 앱 통합이 작동하는 방식을 설명합니다.

이 프로세스는 다음과 같은 6단계로 이루어집니다.

  1. 판매자가 결제 거래를 시작합니다.
  2. 판매자에게 결제 버튼이 표시됩니다.
  3. 고객이 결제 버튼을 누릅니다.

    BobPay (결제 앱) 버튼이 있는 치즈 가게 웹사이트의 다이어그램

  4. 브라우저에서 결제 앱을 실행합니다.

    BobPay 앱이 모달로 실행된 치즈 가게 웹사이트 다이어그램 모달에 배송 옵션과 총비용이 표시됩니다.

  5. 고객이 세부정보 (예: 배송 옵션 또는 주소)를 변경하면 판매자는 변경사항을 반영하여 거래 세부정보를 업데이트합니다.

    고객이 BobPay 앱 모달에서 다른 배송 옵션을 선택하는 모습을 보여주는 다이어그램 두 번째 다이어그램은 BobPay에 표시된 총비용을 업데이트하는 판매자를 보여줍니다.

  6. 고객이 구매를 확인하면 판매자는 결제를 검증하고 거래를 완료합니다.

    고객이

1단계: 판매자가 결제 거래 시작

고객이 구매하기로 결정하면 판매자는 PaymentRequest 객체를 생성하여 결제 거래를 시작합니다. 이 객체에는 거래에 대한 중요한 정보가 포함됩니다.

  • 거래를 처리하는 데 허용되는 결제 수단 및 데이터
  • 세부정보(예: 총 가격(필수), 상품 정보)
  • 판매자가 배송지 주소 및 배송 옵션과 같은 배송 정보를 요청할 수 있는 옵션입니다.
  • 판매자는 청구서 수신 주소, 결제자 이름, 이메일, 전화번호를 요청할 수도 있습니다.
  • 판매자는 PaymentRequest에 선택사항인 배송 유형(shipping, delivery 또는 pickup)을 포함할 수도 있습니다. 결제 앱은 이 정보를 힌트로 사용하여 UI에 올바른 라벨을 표시할 수 있습니다.
const request = new PaymentRequest([{
  supportedMethods: 'https://bobpay.xyz/pay',
  data: {
    transactionId: '****'
  }
}], {
  displayItems: [{
    label: 'Anvil L/S Crew Neck - Grey M x1',
    amount: { currency: 'USD', value: '22.15' }
  }],
  total: {
    label: 'Total due',
    amount: { currency: 'USD', value : '22.15' }
  }
}, {
  requestShipping: true,
  requestBillingAddress: true,
  requestPayerEmail: true,
  requestPayerPhone: true,
  requestPayerName: true,
  shippingType: 'delivery'
});
거래 ID 포함

일부 결제 핸들러는 판매자에게 거래 정보의 일부로 사전에 발급한 거래 ID를 요구할 수 있습니다. 일반적인 통합에는 총 가격을 예약하기 위한 판매자 서버와 결제 핸들러 서버 간의 통신이 포함됩니다. 이렇게 하면 악의적인 고객이 거래 마지막에 유효성 검사를 통해 가격을 조작하고 판매자를 속이는 것을 방지할 수 있습니다.

판매자는 거래 ID를 PaymentMethodData 객체의 data 속성의 일부로 전달할 수 있습니다.

거래 정보를 제공하면 브라우저는 결제 수단 식별자에 따라 PaymentRequest에 지정된 결제 앱의 검색 프로세스를 거칩니다. 이렇게 하면 판매자가 거래를 진행할 준비가 되는 즉시 브라우저에서 실행할 결제 앱을 결정할 수 있습니다.

탐색 프로세스의 작동 방식에 대한 자세한 내용은 결제 수단 설정을 참조하세요.

2단계: 판매자에게 결제 버튼 표시

판매자는 여러 결제 수단을 지원할 수 있지만 고객이 실제로 사용할 수 있는 결제 버튼만 표시해야 합니다. 사용할 수 없는 결제 버튼을 표시하면 사용자 경험이 저하됩니다. 판매자가 PaymentRequest 객체에 지정된 결제 수단이 고객에게 적합하지 않을 것이라고 예측할 수 있는 경우 대체 솔루션을 제공하거나 해당 버튼을 전혀 표시하지 않을 수 있습니다.

판매자는 PaymentRequest 인스턴스를 사용하여 고객에게 사용 가능한 결제 앱이 있는지 쿼리할 수 있습니다.

고객이 사용할 수 있는 결제 앱이 있나요?

고객의 기기에서 결제 앱을 사용할 수 있는 경우 PaymentRequestcanMakePayment() 메서드는 true를 반환합니다. '사용 가능'은 결제 수단을 지원하는 결제 앱이 검색되었고, 플랫폼별 결제 앱이 설치되어 있거나 웹 기반 결제 앱이 등록할 준비가 되었음을 의미합니다.

const canMakePayment = await request.canMakePayment();
if (!canMakePayment) {
  // Fallback to other means of payment or hide the button.
}

3단계: 고객이 결제 버튼 누르기

고객이 결제 버튼을 누르면 판매자는 PaymentRequest 인스턴스의 show() 메서드를 호출하여 결제 UI 실행을 즉시 트리거합니다.

최종 총 가격이 동적으로 설정되는 경우 (예: 서버에서 가져옴) 판매자는 총액이 확인될 때까지 결제 UI 실행을 연기할 수 있습니다.

결제 UI 출시 연기

최종 총 가격이 결정될 때까지 결제 UI 연기 데모를 확인하세요.

결제 UI를 연기하기 위해 판매자는 show() 메서드에 프로미스를 전달합니다. 프로미스가 결정되고 트랜잭션을 시작할 준비가 될 때까지 브라우저에 로드 표시기가 표시됩니다.

const getTotalAmount = async () => {
  // Fetch the total amount from the server, etc.
};

try {
  const result = await request.show(getTotalAmount());
  // Process the result…
} catch(e) {
  handleError(e);
}

show()의 인수로 지정된 프로미스가 없으면 브라우저에서 즉시 결제 UI를 실행합니다.

4단계: 브라우저에서 결제 앱을 실행합니다.

브라우저에서 플랫폼별 또는 웹 기반 결제 앱을 실행할 수 있습니다. Chrome에서 실행할 결제 앱을 결정하는 방법을 자세히 알아보세요.

결제 앱 빌드 방법은 대체로 개발자가 결정하지만 판매자로부터 또는 판매자에게 전송되는 이벤트와 이러한 이벤트와 함께 전달되는 데이터 구조는 표준화됩니다.

결제 앱이 실행되면 1단계의 PaymentRequest 객체에 전달된 거래 정보를 수신합니다. 여기에는 다음이 포함됩니다.

  • 결제 수단 데이터
  • 총 가격
  • 결제옵션

결제 앱은 거래 정보를 사용하여 UI에 라벨을 지정합니다.

5단계: 고객의 작업에 따라 판매자가 거래 세부정보를 업데이트하는 방법

고객은 결제 앱에서 결제 수단 및 배송 옵션과 같은 거래 세부정보를 변경할 수 있습니다. 고객이 변경하는 동안 판매자는 변경 이벤트를 수신하고 거래 세부정보를 업데이트합니다.

판매자가 받을 수 있는 이벤트에는 4가지 유형이 있습니다.

  • 결제 수단 변경 이벤트
  • 배송지 주소 변경 이벤트
  • 배송 옵션 변경 이벤트
  • 판매자 유효성 검사 이벤트

결제 수단 변경 이벤트

결제 앱은 여러 결제 수단을 지원할 수 있으며 판매자는 고객의 선택에 따라 특별 할인을 제공할 수 있습니다. 이 사용 사례를 다루기 위해 결제 수단 변경 이벤트는 판매자가 할인이 적용된 총 가격을 업데이트하고 결제 앱으로 다시 반환할 수 있도록 판매자에게 새 결제 수단을 알릴 수 있습니다.

request.addEventListener('paymentmethodchange', e => {
  e.updateWith({
    // Add discount etc.
  });
});

배송지 주소 변경 이벤트

결제 앱은 고객의 배송지 주소를 선택적으로 제공할 수 있습니다. 이렇게 하면 고객이 양식에 세부정보를 수동으로 입력할 필요가 없고 여러 판매자 웹사이트가 아닌 원하는 결제 앱에 배송지 주소를 저장할 수 있어 편리합니다.

거래가 시작된 후 고객이 결제 앱에서 배송지 주소를 업데이트하면 판매자에게 'shippingaddresschange' 이벤트가 전송됩니다. 이 이벤트는 판매자가 새 주소를 기반으로 배송비를 결정하고 총 가격을 업데이트하여 결제 앱으로 반환하는 데 도움이 됩니다.

request.addEventListener('shippingaddresschange', e => {
  e.updateWith({
    // Update the details
  });
});

판매자가 업데이트된 주소로 배송할 수 없는 경우 결제 앱에 반환된 거래 세부정보에 오류 매개변수를 추가하여 오류 메시지를 표시할 수 있습니다.

배송 옵션 변경 이벤트

판매자는 고객에게 여러 배송 옵션을 제공하고 해당 옵션을 결제 앱에 위임할 수 있습니다. 배송 옵션은 고객이 선택할 수 있는 가격 및 서비스 이름 목록으로 표시됩니다. 예를 들면 다음과 같습니다.

  • 일반 배송 - 무료
  • 빠른 배송 - 5000 KRW

고객이 결제 앱에서 배송 옵션을 업데이트하면 'shippingoptionchange' 이벤트가 판매자에게 전송됩니다. 그러면 판매자는 배송비를 결정하고 총 가격을 업데이트한 후 결제 앱으로 반환할 수 있습니다.

request.addEventListener('shippingoptionchange', e => {
  e.updateWith({
    // Update the details
  });
});

판매자는 고객의 배송지 주소에 따라 배송 옵션을 동적으로 수정할 수도 있습니다. 이 기능은 판매자가 국내외 고객에게 다양한 배송 옵션을 제공하려는 경우에 유용합니다.

판매자 유효성 검사 이벤트

보안을 강화하기 위해 결제 앱은 결제 흐름으로 진행하기 전에 판매자 유효성 검사를 실행할 수 있습니다. 유효성 검사 메커니즘의 설계는 결제 앱에 따라 다르지만 판매자 유효성 검사 이벤트는 자체 유효성 검사에 사용할 수 있는 URL을 판매자에게 알리는 역할을 합니다.

request.addEventListener('merchantvalidation', e => {
  e.updateWith({
    // Use `e.validateURL` to validate
  });
});

6단계: 판매자가 결제를 확인하고 거래 완료

고객이 결제를 승인하면 show() 메서드가 PaymentResponse로 확인되는 프로미스를 반환합니다. PaymentResponse 객체에는 다음 정보가 포함됩니다.

  • 결제 결과 세부정보
  • 배송지 주소
  • 배송 옵션
  • 연락처 정보

이 시점에서 브라우저 UI에는 거래가 아직 완료되지 않았음을 나타내는 로드 표시기가 계속 표시될 수 있습니다.

결제 실패 또는 오류로 인해 결제 앱이 종료되면 show()에서 반환된 프로미스가 거부되고 브라우저가 결제 트랜잭션을 종료합니다.

결제 처리 및 검증

PaymentResponsedetails는 결제 앱에서 반환된 결제 사용자 인증 정보 객체입니다. 판매자는 이 사용자 인증 정보를 사용하여 결제를 처리하거나 확인할 수 있습니다. 이 중요한 프로세스의 작동 방식은 결제 핸들러가 결정합니다.

트랜잭션 완료 또는 재시도

판매자는 거래의 성공 또는 실패 여부를 확인한 후 다음 중 하나를 수행할 수 있습니다.

  • .complete() 메서드를 호출하여 트랜잭션을 완료하고 로드 표시기를 닫습니다.
  • retry() 메서드를 호출하여 고객이 다시 시도할 수 있게 합니다.
async function doPaymentRequest() {
  try {
    const request = new PaymentRequest(methodData, details, options);
    const response = await request.show();
    await validateResponse(response);
  } catch (err) {
    // AbortError, SecurityError
    console.error(err);
  }
}

async function validateResponse(response) {
  try {
    const errors = await checkAllValuesAreGood(response);
    if (errors.length) {
      await response.retry(errors);
      return validateResponse(response);
    }
    await response.complete("success");
  } catch (err) {
    // Something went wrong…
    await response.complete("fail");
  }
}
// Must be called as a result of a click
// or some explicit user action.
doPaymentRequest();

다음 단계