목표
1. paymentwall에서 사용하는 API 조사 & 비교 (subscription 중심)
- Checkout API
- Direct API - Brick Direct API
- 사용할 API 선택

import com.paymentwall.java.*;
Config.getInstance().setLocalApiType(Config.API_GOODS);
Config.getInstance().setPublicKey("YOUR_PROJECT_KEY");
Config.getInstance().setPrivateKey("YOUR_SECRET_KEY");
Pingback pingback = new Pingback(request.getParameterMap(), request.getRemoteAddr());
if (pingback.validate(true)) {
String goods = pingback.getProductId();
String userId = pingback.getUserId();
if (pingback.isDeliverable()) {
// deliver Product to user with userId
} else if (pingback.isCancelable()) {
// withdraw Product from user with userId
}
return "OK";
} else {
return pingback.getErrorSummary();
}
http://www.yourserver.com/pingback_path?uid=pwuser&goodsid=gold_membership&slength=3&speriod=day&type=0&ref=b1493048890&sign_version=2&sig=d94b23ba8585f29978706dd1b153ead9
private void sendPost() {
HttpPost post = new HttpPost("https://api.paymentwall.com/api/delivery");
post.addHeader("X-ApiKey", “<PROJECT_SECRET_KEY>”);
// add request parameter, form parameters
List<NameValuePair> urlParameters = new ArrayList<NameValuePair>();
urlParameters.add(new BasicNameValuePair("payment_id", "b199488072"));
urlParameters.add(new BasicNameValuePair("merchant_reference_id", "w199488072"));
urlParameters.add(new BasicNameValuePair("type", "digital"));
urlParameters.add(new BasicNameValuePair("status", "order_placed"));
urlParameters.add(new BasicNameValuePair("estimated_delivery_datetime", "2015/01/15 15:00:00 +0300"));
urlParameters.add(new BasicNameValuePair("estimated_update_datetime", "2015/01/15 11:00:00 +0300"));
urlParameters.add(new BasicNameValuePair("refundable", "true"));
urlParameters.add(new BasicNameValuePair("details", "Item was delivered to the user account and via email"));
urlParameters.add(new BasicNameValuePair("shipping_address[email]", "test@paymentwall.com"));
urlParameters.add(new BasicNameValuePair("reason", "none"));
urlParameters.add(new BasicNameValuePair("attachments[0]", "@/usr/local/www/content/proof/b63400368/1.png"));
urlParameters.add(new BasicNameValuePair("attachments[1]", "@/usr/local/www/content/proof/b63400368/2.png"));
try {
post.setEntity(new UrlEncodedFormEntity(urlParameters));
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = httpClient.execute(post);
System.out.println(EntityUtils.toString(response.getEntity()));
}
catch(IOException ex) {
System.out.println(ex.getMessage());
}
}
GET https://api.paymentwall.com/api/subscription
Config.getInstance().setLocalApiType(Config.API_GOODS);
Config.getInstance().setPublicKey("YOUR_PROJECT_KEY");
Config.getInstance().setPrivateKey("YOUR_SECRET_KEY");
WidgetBuilder widgetBuilder = new WidgetBuilder("USER_ID", "p1");
widgetBuilder.setProduct(
new ProductBuilder("YOUR_PRODUCT_ID") {
{
setAmount(0.99);
setCurrencyCode("USD");
setName("YOUR_PRODUCT_NAME");
setProductType(Product.TYPE_SUBSCRIPTION);
setPeriodType("month");
setPeriodLength(3);
}
}.build());
widgetBuilder.setExtraParams(new LinkedHashMap<String, String>(){
{
put("email", "YOUR_CUSTOMER_EMAIL");
put("history[registration_date]","REGISTRATION_DATE");
put("ps","all"); // Replace it with specific payment system short code for single payment methods
}
});
Widget widget = widgetBuilder.build();
return widget.getUrl();
brick은 paymentwall이 제공하는 순수 신용카드 결제 솔루션입니다. 호스팅된 체크아웃 경험을 위한 신용카드 게이트웨이로서 Widget API와 Checkout API에 통합되었습니다.
다음의 파라미터들을 얻기 위해 default form 을 사용 할 지, own form을 사용할 지 선택할 수 있습니다.
이 두 파라미터를 백엔드로 전송해야 합니다.
a. 지불 세부 정보를 default form으로 토큰화하기
<!-- Brick Form -->
<script type="text/javascript" src="https://api.paymentwall.com/brick/build/brick-default.1.5.0.min.js"></script>
<!-- id를 사용하여 div 태그를 생성하고 별도의 script의 있는 'container'값에 id를 넣어 일치시켜주세요. -->
<div id="payment-form-container"></div>
<!-- 당신의 Brick project key를 'publick_key'에 넣어주세요.-->
<script>
var brick = new Brick({
public_key: 'YOUR_PUBLIC_KEY', // please update it to Brick live key before launch your project
amount: 9.99,
currency: 'USD',
container: 'payment-form-container',
action: '/YOUR-CHARGE-ACTION',
form: {
merchant: 'Paymentwall',
product: 'Gold Membership',
pay_button: 'Pay',
show_zip: true, // show zip code
show_cardholder: true // show card holder name
}
});
brick.showPaymentForm(function(data) {
// handle success
}, function(errors) {
// handle errors
});
</script>
Brick objcect는 결제 세부 정보를 수집하고 fingerprint 생성 및 토큰화를 자동으로하도록 해줍니다.
b. 지불 세부 정보를 own form으로 토큰화하기
<!--html tags-->
<form id="brick-creditcard-form" action="billing.php" method="POST">
<input name="custom_parameter" type="hidden" value="custom_value"/>
<div>
<label>
<span>Card number</span>
<input data-brick="card-number" type="text" id="card-number"/>
</label>
</div>
<div>
<label>
<span>Card expiration</span>
<input data-brick="card-expiration-month" type="text" size="2" id="card-exp-month"/> /
<input data-brick="card-expiration-year" type="text" size="4" id="card-exp-year"/>
</label>
</div>
<div>
<label>
<span>Card CVV</span>
<input data-brick="card-cvv" type="text" id="card-cvv"/>
</label>
</div>
<button type="submit">Submit</button>
</form>
<!-- 토큰화를 구현하기 위한 Brick.js를 포함합니다. 이렇게 하면 신용카드 정보를 토큰화할 필요가 없습니다.
https://api.paymentwall.com/brick/에서 직접 로드할 수 있습니다. -->
<script type="text/javascript" src="https://api.paymentwall.com/brick/brick.1.4.js"></script>
<!-- 다음 단계는 새 스크립트 태그를 생성하여 Brick object를 선언하고
Dashboard에서 찾을 수 있는 Brick 키로 API 자격 증명을 설정하는 것입니다.
jQuery를 사용합니다. -->
<script>
var $form = $('#brick-creditcard-form');
var brick = new Brick({
public_key: 't_365891948ea844de751301cbcc1897', // please update it to Brick live key before launch your project
form: { formatter: true }
}, 'custom');
</script>
<!-- 토큰화를 구현하고 당신의 서버에 정보를 보내려면 이 스크립트를 추가하여 지불 세부 정보를 수집해야 합니다. -->
<script>
$form.submit(function(e) {
e.preventDefault();
$form.find('button').prop('disabled', true); // prevent repeat click
brick.tokenizeCard({ // Tokenize payment details
card_number: $('#card-number').val(),
card_expiration_month: $('#card-exp-month').val(),
card_expiration_year: $('#card-exp-year').val(),
card_cvv: $('#card-cvv').val()
}, function(response) {
if (response.type == 'Error') { // faile to create token
// handle errors
} else { // token created successfully
$form.append($('<input type="hidden" name="brick_token"/>').val(response.token));
$form.append($('<input type="hidden" name="brick_fingerprint"/>').val(Brick.getFingerprint()));
$form.get(0).submit(); // submit token and fingerprint to your server
}
});
return false;
});
</script>
a. subscription request
백엔드에서 one-time token 및 fingerprint를 수집한 것으로 가정합니다. 파라미터 형식은 subscription API를 참조하세요.
JAVA
Config.getInstance().setPublicKey("YOUR_PUBLIC_KEY");
Config.getInstance().setPrivateKey("YOUR_PRIVATE_KEY");
LinkedHashMap<String, String> subscriptionmap = new LinkedHashMap<String, String>();
subscriptionmap.put("token", request.getParameter("brick_token"));
subscriptionmap.put("email", request.getParameter("email"));
subscriptionmap.put("currency", "USD");
subscriptionmap.put("amount", "9.99");
subscriptionmap.put("description", "YOUR-DESCRIPTION");
subscriptionmap.put("fingerprint", request.getParameter("brick_fingerprint"));
subscriptionmap.put("plan", "YOUR-PRODUCT-ID");
subscriptionmap.put("period", "week");
subscriptionmap.put("period_duration", "1");
Subscription subscription = new Subscription();
subscription = (Subscription) subscription.create(subscriptionmap);
b. trial period 설정
고객에게 trial 사용 기간을 제공하려면 제품에 trial 제품을 추가할 수 있습니다.
JAVA
Config.getInstance().setPublicKey("YOUR_PUBLIC_KEY");
Config.getInstance().setPrivateKey("YOUR_PRIVATE_KEY");
LinkedHashMap<String, String> subscriptionmap = new LinkedHashMap<String, String>();
subscriptionmap.put("token", request.getParameter("brick_token"));
subscriptionmap.put("email", request.getParameter("email"));
subscriptionmap.put("currency", "USD");
subscriptionmap.put("amount", "9.99");
subscriptionmap.put("description", "YOUR-DESCRIPTION");
subscriptionmap.put("fingerprint", request.getParameter("brick_fingerprint"));
subscriptionmap.put("plan", "YOUR-PRODUCT-ID");
subscriptionmap.put("period", "week");
subscriptionmap.put("period_duration", "1");
subscriptionmap.put("trial[amount]", "0.5");
subscriptionmap.put("trial[currency]", "USD");
subscriptionmap.put("trial[period]", "day");
subscriptionmap.put("trial[period_duration]", "3");
Subscription subscription = new Subscription();
subscription = (Subscription) subscription.create(subscriptionmap);
c. Subscription response object
subscription request가 성공적으로 수행되면 subscription의 세부 정보가 포함된 subscription response object를 받게 됩니다. subscription response attributes를 참고하세요.
각 subscription에는 subscription histroy의 결제를 나타내는 여러 개의 charge id가 있습니다. paymentwall 즉시 결제 알림, pingback은 subscription request가 이루어지면 즉시 전송됩니다. 당신의 배송은 pingback의 type 에 따라 수행되어야 합니다. 또한 자신의 매개 파라미터를 subscription request의 추가 파라미터로 정의할 수 있으며, 이 파라미터는 결제 요청의 투명한 전송을 위한 custom pingback 파라미터로 설정할 수 있습니다.
d. Subscription Schedule
사용자가 Subscription을 통해 처음 결제하면 Subscription이 만료될 때까지 반복 청구할 수 있습니다. Subscription Schedule에 따라 사용자에게 자동으로 요금을 부과합니다. 사용자가 청구될 때마다 새 pingback이 전송됩니다. 마지막 요금 청구가 발생하면 Paymentwall은 가입이 만료되었음을 의미하는 type=13 의 pingback을 전송합니다.
e. Subscription failure
사용자가 자금이 부족하거나 다른 이유로 결제에 실패할 경우, Paymentwall은 2회 재시도(총 3회)하여 사용자에게 요금을 청구합니다. Payment Status API는 Subscription의 활성 상태를 보고하고 date_next는 다음 시도 날짜를 포함합니다. 모든 시도가 실패하면 Paymentwall이 Subscription을 중지합니다. Subscription이 중지되면 다음 예약된 지불에 대해 사용자에게 요금을 청구하는 시도가 더 이상 처리되지 않습니다. 이 이벤트의 경우 Paymentwall은 type=14 의 pingback을 전송합니다. 이는 구독 결제가 실패했음을 의미합니다.
f. Subscription cancellation
아래 스크립트를 사용하거나 cancellation API를 사용하여 Subscription을 취소할 수도 있습니다.
JAVA
Subscription subscription = new Subscription("SUBSCRIPTION_ID");
subscription = (Subscription)(subscription.cancel());
return subscription.isActive();
3D Secure(Visa 인증, MasterCard SecureCode)는 신용 카드 결제를 위한 추가 보안 단계로 사기를 방지하는 데 도움이 됩니다.
a. Enable 3D secure
Subscription request에서 secure=1을 추가 파라미터로 보내면 고객의 카드 발급 은행에서 제공하는 3D secure form으로 고객을 리디렉션할 수 있는 리디렉션 URL이 포함된 response가 옵니다. 3D secure 기능을 구현하려면 다음 조정을 수행해야 합니다.
b. Collect secure token
결제가 3D secure 결제 단계에 등록되면, 결제자는 3D secure form으로 본인 확인이 필요하며, 이는 본인의 카드 비밀번호 또는 인증 코드가 포함된 SMS 메시지 등이 될 수 있습니다.
고객을 위한 3D secure form을 표시하는 것이 첫 번째 단계입니다. 그 후, 지불자가 두 번째 charge request에 필요한 3D 보안 단계를 완료하면 brick_secure_token과 brick_charge_id가 http request object에 포함됩니다.
결제 양식에 따라 구현 방법이 다릅니다.
default payment form을 사용하는 경우 이 부분은 default payment form 자체로 처리됩니다. 계속하려면 re-submit charge request를 참조하세요.
custom payment form을 선호하는 가맹점의 경우 다음 단계가 필요합니다.
request에 위의 두 가지 추가 파라미터를 추가해야 합니다.
secure_redirect_url은 3D 보안 단계를 완료한 후 고객이 리디렉션되는 URL입니다.
secure_return_method의 값을 url로 설정해야 html 형식이 아닌 리디렉션 URL을 얻을 수 있습니다.
JAVA
... // your other charge parameters
chargemap.put("secure_redirect_url", "YOUR-SECURE-REDIRECT-URL");
chargemap.put("secure_return_method", "url");
원래 request의 brick_fingerprint 및 brick_firck_url은 secure_finger_url에 포함되어 이후에 두 번째 request로 전달될 수 있어야 합니다. 다음은 샘플입니다.
secure_redirect_url: http://your-domain/your-secure-redirect-url?brick_token=ot_4ca5cbda3D4af3444759e4934dd25717&brick_fingerprint=satiO3yvBDuPMEZUJep4vKuqVav5VxAT
3D secure를 활성화하면 리디렉션 URL이 포함된 response object를 얻을 수 있습니다. 아래는 샘플입니다.
{
"success":0,
"secure":{
"redirect":"https:\/\/api.paymentwall.com\/api\/brick\/redirect\/3Ds\/fe989d17-5632-11e7-bfd3-002590852bf4\/44ea915ab53D78f96b3Dd485e7a5f8d2441572876f7a2eb88f5101cb197adcc9"
}
}
이를 사용하여 고객을 3D secure page로 리디렉션할 수 있습니다. 계속하려면 다음 단계를 참조하십시오.
원래 request에 secure_return_method가 포함되어 있지 않은 경우에만 다음 추가 단계가 필요합니다. URL을 리디렉션하는 대신 3D Secure의 html 형식을 사용할 수 있습니다.
{
"success":0,
"secure":{
"formHTML":"<div><form action=\"https:\/\/api.paymentwall.com\/api\/brick\/secure-test-bank-page?public_key=t_a93Db6bffafdda5c57ab48296fdbba\" method=\"POST\"><input type=\"hidden\" name=\"PaReq\" value=\"to_validate_this\"><input type=\"hidden\" name=\"MD\" value=\"t34451493976105_test\"><input type=\"hidden\" name=\"TermUrl\" value=\"https:\/\/api.paymentwall.com\/api\/brick\/secure-payment?public_key=a3Dff98c34722f0e130a68e6b4c9da56&secure_redirect_url=http%3A%2F%2Fpaymentwall.com%2Fbrick%2F3Dsecure%3Fbrick_token%3Dot_4ca5cbda3D4af3444759e4934dd25717%26brick_fingerprint%3DsatiO3yvBDuPMEZUJep4vKuqVav5VxAT\"><\/form><\/div>"
}
}
formHTML 속성 값을 얻고 결제 페이지에 삽입하여 직접 제출하면 결과적으로 3D 보안 양식이 새 탭에 표시됩니다.
<script>
document.getElementById("3Ds_form_container").getElementsByTagName("form")[0].submit();
// 3Ds_form_container is the place where 3D secure form is embedded in
</script>
당신의 backend에서 상세 3D Secure를 처리하세요.
brick_secure_token 및 brick_charge_id는 지급인이 3D Secure 결제 단계를 확인할 때마다 POST를 통해 secure_redirect_url로 전송되므로 이제 다음 단계를 계속 진행할 수 있습니다. 서버에서 SameSite 쿠키 속성 을 구현하는 경우 Paymentwall은 POST를 통해 brick_secure_token을 전송합니다. secure_redirect_url에 대한 인증이 필요한 경우, Paymentwall에서 사용자를 보낼 때 쿠키가 무시될 수 있습니다. 이 경우에는 secure_redirect_url에 대한 인증을 요구하지 않는 것이 좋습니다.
c. Re-submit charge request
3D Secure이 설정된 결제에는 두 개 이하의 파라미터가 포함된 다른 request가 필요합니다.
두 가지 파라미터가 백엔드로 전달되면 두 번째 request에서 이 파라미터들을 직접 추가할 수 있습니다.
JAVA
LinkedHashMap<String,String> chargemap = new LinkedHashMap<String, String>();
... // your original charge request parameters
if (request.getParameter("brick_charge_id")!=null &&request.getParameter("brick_secure_token")!=null){
chargemap.put("charge_id", request.getParameter("brick_charge_id"));
chargemap.put("secure_token",request.getParameter("brick_secure_token"));
}
For EMV 3DS (3DS 2.0) you will need to send an additional param reference_id which is collected by Brick.js into original charge request
<?php
// ....
$cardInfo = [
'token' => $_POST['brick_token'], // Collected by Brick.js when customer click pay
'fingerprint' => $parameters['brick_fingerprint'], // Collected by Brick.js when customer click pay
'email' => 'johndoe@test.com',
'amount' => 9.99,
'currency' => 'USD',
'description' => 'Order #123',
// ....
];
if (isset($_POST['brick_reference_id'])) {
$cardInfo['reference_id'] = $_POST['brick_reference_id']; // Collected by Brick.js when customer click pay with 3DS 2.0 enabled
}
?>
Brick은 비동기식 결제 알림, pingback을 각 결제에 대한 추가 확인으로 보냅니다.
a. Handle Pingback
당신의 서버에서 다음 코드를 온라인 서버 인터페이스로 설정하여 Pingback과 상호 작용합니다.
JAVA
import com.paymentwall.java.*;
Config.getInstance().setLocalApiType(Config.API_GOODS);
Config.getInstance().setPublicKey("YOUR_PROJECT_KEY");
Config.getInstance().setPrivateKey("YOUR_SECRET_KEY");
Pingback pingback = new Pingback(request.getParameterMap(), request.getRemoteAddr());
if (pingback.validate(true)) {
String goods = pingback.getProductId();
String userId = pingback.getUserId();
if (pingback.isDeliverable()) {
// deliver Product to user with userId
} else if (pingback.isCancelable()) {
// withdraw Product from user with userId
}
return "OK";
} else {
return pingback.getErrorSummary();
}
pingback 요청에는 일반적으로 제품 배달을 수행하는 데 필요한 모든 정보가 포함됩니다. 또한 Paymentwall은 일련의 reverse된 파라미터들을 특정 요구를 위한 custom pingback 파라미터로 제공하며, 파라미터 전송을 구현하기 위해 custom pingback 파라미터로 자체 파라미터를 추가할 수도 있습니다.
다음은 기본 형식의 샘플입니다.
http://www.yourserver.com/pingback_path?uid=pwuser&goodsid=gold_membership&slength=&speriod=&type=0&ref=b1493096790&sign_version=2&sig=d94b23ba8585f29978706dd1b153ead9
Brick payment의 pingback types for risk review를 참조하세요.
pingback을 확인한 후 당신의 서버는 항상 배송 프로세스를 진행하고 response의 body에서 OK로만 응답할 수 있습니다.
Brick이 올바르게 작동하도록 merchant dashboard에서 프로젝트를 구성합니다.
Private API Key는 이 API를 사용하여 custom HTTPS header X-ApiKey의 값으로 전송됩니다. server-to-server calls에만 사용할 수 있으며 최종 사용자에게 노출되어서는 안 됩니다. 만약 당신이 paymentwall의 API 라이브러리를 사용하고 있다면, 이것은 자동으로 될 것입니다.
parameters
Checkout API는 ps 파라미터에 따라서 결제 수단을 설정 할 수 있고, 유저의 IP에 따라서 결제 수단이 결정됩니다. 반면 Brick Direct API는 순수 신용카드 결제 솔루션입니다.
Checkout API는 payment page를 iframe으로 구성하여 redirect하여 보여주고,
Brick Direct API는 html에 payment page 관련 div태그를 만들어 brick.js의 conatiner에 div id값을 넣어주어 생성합니다.
두 API 모두 사용자가 결제를 완료하면 paymentwall로부터 pingback을 받습니다.
자체 parameter추가도 가능합니다.
Checkout API는 window.postMessage() 매카니즘을 사용하여 client-side callback을 보내고 결제 성공 관련 동작을 수행하게 할 수 있습니다.
Brick Direct API는 brick.js를 이용하여 3D Secure을 완료하면 secure_redirect_url에 설정된 url로 redirect하여 결제 성공 관련 동작을 수행할 수 있습니다.
Brick Direct API는 3D Secure 관련 추가 작업이 필요합니다.