빌링 서비스 및 오픈소스 도구 PG 연계 테스트 설계서 - SeungpilPark/uEngine-bill GitHub Wiki
Abstract Plugin
PG 사의 는 승인, 구매, Direct 신용카드 구매, 캡쳐, 환불 API 를 제공하기 위해 플러그인은 아래 형식의 abstract 코드 룰을 따르기로 한다. 아래의 코드는 killbill 의 플러그인 api 이다.
public interface PaymentPluginApi {
/**
* Authorize a specific amount in the Gateway.
*
* @param kbAccountId killbill accountId
* @param kbPaymentId killbill payment id
* @param kbTransactionId killbill transaction id
* @param kbPaymentMethodId killbill payment method id
* @param amount amount to charge
* @param currency currency
* @param properties custom properties for the gateway
* @param context call context
* @return information about the authorization in the gateway
* @throws PaymentPluginApiException If any unexpected error occurs
*/
public PaymentTransactionInfoPlugin authorizePayment(UUID kbAccountId, UUID kbPaymentId, UUID kbTransactionId, UUID kbPaymentMethodId, BigDecimal amount, Currency currency, Iterable<PluginProperty> properties, CallContext context)
throws PaymentPluginApiException;
/**
* Capture a specific amount in the Gateway.
*
* @param kbAccountId killbill accountId
* @param kbPaymentId killbill payment id
* @param kbTransactionId killbill transaction id
* @param kbPaymentMethodId killbill payment method id
* @param amount amount to charge
* @param currency currency
* @param properties custom properties for the gateway
* @param context call context
* @return information about the capture in the gateway
* @throws PaymentPluginApiException If any unexpected error occurs
*/
public PaymentTransactionInfoPlugin capturePayment(UUID kbAccountId, UUID kbPaymentId, UUID kbTransactionId, UUID kbPaymentMethodId, BigDecimal amount, Currency currency, Iterable<PluginProperty> properties, CallContext context)
throws PaymentPluginApiException;
/**
* Charge a specific amount in the Gateway.
*
* @param kbAccountId killbill accountId
* @param kbPaymentId killbill payment id
* @param kbTransactionId killbill transaction id
* @param kbPaymentMethodId killbill payment method id
* @param amount amount to charge
* @param currency currency
* @param properties custom properties for the gateway
* @param context call context
* @return information about the payment in the gateway
* @throws PaymentPluginApiException If any unexpected error occurs
*/
public PaymentTransactionInfoPlugin purchasePayment(UUID kbAccountId, UUID kbPaymentId, UUID kbTransactionId, UUID kbPaymentMethodId, BigDecimal amount, Currency currency, Iterable<PluginProperty> properties, CallContext context)
throws PaymentPluginApiException;
/**
* Void an authorization in the Gateway.
*
* @param kbAccountId killbill accountId
* @param kbPaymentId killbill payment id
* @param kbTransactionId killbill transaction id
* @param kbPaymentMethodId killbill payment method id
* @param properties custom properties for the gateway
* @param context call context
* @return information about the capture in the gateway
* @throws PaymentPluginApiException If any unexpected error occurs
*/
public PaymentTransactionInfoPlugin voidPayment(UUID kbAccountId, UUID kbPaymentId, UUID kbTransactionId, UUID kbPaymentMethodId, Iterable<PluginProperty> properties, CallContext context)
throws PaymentPluginApiException;
/**
* Credit a specific amount in the Gateway.
*
* @param kbAccountId killbill accountId
* @param kbPaymentId killbill payment id
* @param kbTransactionId killbill transaction id
* @param kbPaymentMethodId killbill payment method id
* @param amount amount to credit
* @param currency currency
* @param properties custom properties for the gateway
* @param context call context
* @return information about the credit in the gateway
* @throws PaymentPluginApiException If any unexpected error occurs
*/
public PaymentTransactionInfoPlugin creditPayment(UUID kbAccountId, UUID kbPaymentId, UUID kbTransactionId, UUID kbPaymentMethodId, BigDecimal amount, Currency currency, Iterable<PluginProperty> properties, CallContext context)
throws PaymentPluginApiException;
/**
* Process a refund against a given payment.
*
* @param kbAccountId killbill accountId
* @param kbPaymentId killbill payment id
* @param kbTransactionId killbill transaction id
* @param kbPaymentMethodId killbill payment method id
* @param amount refund amount
* @param currency currency
* @param properties custom properties for the gateway
* @param context call context
* @return information about the refund in the gateway
* @throws PaymentPluginApiException If any unexpected error occurs
*/
public PaymentTransactionInfoPlugin refundPayment(UUID kbAccountId, UUID kbPaymentId, UUID kbTransactionId, UUID kbPaymentMethodId, BigDecimal amount, Currency currency, Iterable<PluginProperty> properties, CallContext context)
throws PaymentPluginApiException;
/**
* Retrieve information about a given payment.
*
* @param kbAccountId killbill accountId
* @param kbPaymentId killbill payment id
* @param properties custom properties for the gateway
* @param context call context
* @return information about the payment in the gateway
* @throws PaymentPluginApiException If any unexpected error occurs
*/
public List<PaymentTransactionInfoPlugin> getPaymentInfo(UUID kbAccountId, UUID kbPaymentId, Iterable<PluginProperty> properties, TenantContext context)
throws PaymentPluginApiException;
/**
* Search payments.
* <p>
* The search is plugin specific, there is no constraint on how the searchKey should be interpreted.
*
* @param offset the offset of the first result
* @param limit the maximum number of results to retrieve
* @param properties custom properties for the gateway
* @param context call context
* @return payments matching the search key
* @throws PaymentPluginApiException If any unexpected error occurs
*/
public Pagination<PaymentTransactionInfoPlugin> searchPayments(String searchKey, Long offset, Long limit, Iterable<PluginProperty> properties, TenantContext context)
throws PaymentPluginApiException;
/**
* Add a payment method for a Killbill account in the gateway.
* <p>
* Note: the payment method doesn't exist yet in Killbill when receiving the call in
* the plugin (kbPaymentMethodId is a placeholder).
*
* @param kbAccountId killbill accountId
* @param paymentMethodProps payment method details
* @param setDefault set it as the default payment method in the gateway
* @param properties custom properties for the gateway
* @param context call context
* @throws PaymentPluginApiException If any unexpected error occurs
*/
public void addPaymentMethod(UUID kbAccountId, UUID kbPaymentMethodId, PaymentMethodPlugin paymentMethodProps, boolean setDefault, Iterable<PluginProperty> properties, CallContext context)
throws PaymentPluginApiException;
/**
* Delete a payment method in the gateway.
*
* @param kbAccountId killbill accountId
* @param kbPaymentMethodId killbill payment method id
* @param properties custom properties for the gateway
* @param context call context
* @throws PaymentPluginApiException If any unexpected error occurs
*/
public void deletePaymentMethod(UUID kbAccountId, UUID kbPaymentMethodId, Iterable<PluginProperty> properties, CallContext context)
throws PaymentPluginApiException;
/**
* Get payment method details for a given payment method.
*
* @param kbAccountId killbill account id
* @param kbPaymentMethodId killbill payment method id
* @param properties custom properties for the gateway
* @param context call context
* @return PaymentMethodPlugin info for the payment method
* @throws PaymentPluginApiException If any unexpected error occurs
*/
public PaymentMethodPlugin getPaymentMethodDetail(UUID kbAccountId, UUID kbPaymentMethodId, Iterable<PluginProperty> properties, TenantContext context)
throws PaymentPluginApiException;
/**
* Set a payment method as default in the gateway.
*
* @param kbAccountId killbill accountId
* @param kbPaymentMethodId killbill payment method id
* @param properties custom properties for the gateway
* @param context call context
* @throws PaymentPluginApiException If any unexpected error occurs
*/
public void setDefaultPaymentMethod(UUID kbAccountId, UUID kbPaymentMethodId, Iterable<PluginProperty> properties, CallContext context)
throws PaymentPluginApiException;
/**
* This is used to see the view of paymentMethods kept by the plugin or the view of
* existing payment method on the gateway.
* <p>
* Sometimes payment methods have to be added directly to the gateway for PCI compliance issues
* and so Kill Bill needs to refresh its state.
*
* @param kbAccountId killbill accountId
* @param refreshFromGateway fetch the list of existing payment methods from gateway -- if supported
* @param properties custom properties for the gateway
* @param context call context
* @return all payment methods for that account
* @throws PaymentPluginApiException If any unexpected error occurs
*/
public List<PaymentMethodInfoPlugin> getPaymentMethods(UUID kbAccountId, boolean refreshFromGateway, Iterable<PluginProperty> properties, CallContext context)
throws PaymentPluginApiException;
/**
* Search payment methods
* <p>
* The search is plugin specific, there is no constraint on how the searchKey should be interpreted.
*
* @param offset the offset of the first result
* @param limit the maximum number of results to retrieve
* @param properties custom properties for the gateway
* @param context call context
* @return payment methods matching the search key
* @throws PaymentPluginApiException If any unexpected error occurs
*/
public Pagination<PaymentMethodPlugin> searchPaymentMethods(String searchKey, Long offset, Long limit, Iterable<PluginProperty> properties, TenantContext context)
throws PaymentPluginApiException;
/**
* This is used after Killbill decided to refresh its state from the gateway
*
* @param kbAccountId killbill accountId
* @param paymentMethods the list of payment methods
* @param properties custom properties for the gateway
* @param context call context
* @throws PaymentPluginApiException If any unexpected error occurs
*/
public void resetPaymentMethods(UUID kbAccountId, List<PaymentMethodInfoPlugin> paymentMethods, Iterable<PluginProperty> properties, CallContext context)
throws PaymentPluginApiException;
/**
* Build metadata for the client to create a redirect form
*
* @param kbAccountId killbill accountId
* @param customFields form fields
* @param properties custom properties for the gateway
* @param context call context
* @return redirect form metadata
* @throws PaymentPluginApiException If any unexpected error occurs
*/
public HostedPaymentPageFormDescriptor buildFormDescriptor(UUID kbAccountId, Iterable<PluginProperty> customFields, Iterable<PluginProperty> properties, CallContext context)
throws PaymentPluginApiException;
/**
* Process a notification from the gateway
* <p>
* This potentially does more than just deserialize the payload. The plugin may have to acknowledge it
* with the gateway.
*
* @param notification serialized notification object
* @param properties custom properties for the gateway
* @param context call context
* @return gateway notification object used to build the response to the gateway
* @throws PaymentPluginApiException If any unexpected error occurs
*/
public GatewayNotification processNotification(String notification, Iterable<PluginProperty> properties, CallContext context)
throws PaymentPluginApiException;
}
Payment Flows 설계
PG 사의 특징에 따라(대부분 국가별 기능 제한에 따라) 결제 플로우는 Hosted Payment Pages (HPP)와 게이트웨이 통합이라는 두 가지 흐름이 있다.
- HPP 플로우는 사용자를 제 3 자 웹 사이트로 리디렉션하거나 양식 또는 iframe을 호스팅하여 타사 웹 사이트에 정보를 제출하여 지불이 완전히 아웃소싱되는 경우이다.
- 게이트웨이 통합 플로우는 고객이 웹 사이트를 떠나지 않고 빌링 프레임워크가 게이트웨이 API를 호출하여 직접 지불을 처리하는 경우이다.
아래의 흐름도에서 다음과 같은 액터를 고려해야 한다. :
- 브라우저 : 자바스크립트 등을 사용해 결제 흐름 시작하기
- 판매자 사이트 (Merchants Site) : 고객이 주문을 받고 결제 시스템(빌링 프레임워크)을 보호하는 웹 사이트
- 결제 : 다양한 결제 수단에 대한 API를 제공하는 결제 시스템 (즉, 특정 PG 결제 플러그인)
- 지불 제공자 : Payment Service Provider 라고도 하는데, 지불 게이트웨이 또는 단순히 게이트웨이라고 불리는 이것은 지불을 처리 할 주체이다.
- 액세스 제어 서버 (옵션) : 3D 보안 체크 아웃의 경우 사용자가 일부 제 3 자 엔티티로 리디렉션되어 사용자의 결제 흐름을 추적 할 수 있는 사용자 정의 정보를 입력한다.
HPP 플로우 중에 고객은 자신의 지불 방법 정보 (주요 판매자 사이트 또는 제 3 자 사이트)를 입력 한 다음 해당 정보를 포함하는 양식을 제출하여 지불한다. 이 때 사용자에게 지불 양식을 표시하는 데 필요한 정보를 buildFormDescriptor API 등을 호출하게 할 수 있으면 편리할 수 있다.
API 호출의 결과는 HPP 통합의 유형에 따라 다르다.
지불 양식이 사용자의 웹 사이트에서 호스팅되는 경우 (양식 데이터가 제 3 자 웹 사이트로 전송되는 경우), 응답에는 설정할 정확한 필드가 나열된다. (이름은 게이트웨이에 따라 다르다. 예를 들어 금액 대신 합계 필드가 필요함) 및 숨겨진 필드 (예 : merchantId 값)가 필요하다. 예를 들어 PayPal Payments Standard 버튼의 경우 API는 cmd, hosted_button_id 및 submit 필드의 값을 반환한다. 브라우저는 지불 정보를 지불 제공자에게 제출하고, 지불 제공자는 성공 또는 실패시 고객을 방문 페이지로 리디렉션한다.
결제 양식이 타사 웹 사이트에서 완전히 호스팅되는 경우 응답에 사용자를 리디렉션 할 URL이 포함횐다. 이 URL은 주문 정보를 게이트웨이에 제출하거나 쿼리 매개 변수를 통해 특수 URL을 구성하여 지불 플러그인에서 생성 된 일반 또는 고유 항목 일 수 있다. 예를 들어 Dwolla Forms의 경우 API는 https://forms.dwollalabs.com/john과 같은 URL을 반환한다. 브라우저는 결제 공급자 웹 사이트로 리디렉션되어 고객이 결제를 완료 한 다음 성공 또는 실패시 방문 페이지로 다시 리디렉션됩니다.
다음은 몇 가지 일반적인 장면이다.
게이트웨이 통합의 경우, 지불에 대한 진입 점은 createAuthorization (신용 카드 권한 부여), createPurchase (신용 카드 승인 및 캡처, ACH 이전 시작, Bitcoin wallet 등) 또는 createCredit (이전 지불에 대한 참조없이 페이먼트 메소드에 입금하기 위해) API가 있다. 지불 상태는 플러그인을 호출하기 전에 * _INIT 상태가 된다.
일반적으로 트랜잭션은 대부분의 게이트웨이가 동기식 API를 제공하므로 터미널 상태로 종료된다. 그러나 결제 방법에 따라 플러그인에서 상태를 PENDING으로 설정할 수 있어야 한다. 예를 들어, 3D 보안 트랜잭션의 경우 (사용자가 리디렉션 된 후 지급인이 결제를 확인할 때까지), 자동 이체 (예 : ACH, 승인을 받기까지 며칠이 걸림) 또는 Bitcoin 전송 (트랜잭션이 블록 체인에 의해 확인 될 때까지). 또한 일부 게이트웨이는 항상 동기 응답 (예 : Adyen을 사용하여 자금을 캡처 할 때)을 제공하지는 않는다.이 경우 플러그인은 지불을 터미널 상태로 전환하기 위해 비동기 통지에 의존해야 한다.