Tích hợp softbank payment cho trang web của bạn
White

Lưu Quốc Khánh viết ngày 09/07/2019

Mở đầu

Như tiêu đề thì hôm nay mình sẽ giới thiệu cho các bạn cách để có thể tích hợp thanh toán qua softbank payment.
Softbank payment cung cấp cho chúng ta 2 cách để tích hợp, đó là:

  • Sử dụng api của ngân hàng
  • Request qua trang trung gian của ngân hàng

Hôm nay mình sẽ giới thiệu cách bảo mật hơn đó là sử dụng 3D secure (3Ds) được cung cấp từ phía ngân hàng
3Ds là cách thức bảo mật hiểu đơn giản nó như kiểu xác thực 2 bước vậy, tức là để thanh toán được ngoài việc ngọi người nhập thông tin thẻ ra thì phải nhập thêm một mã bí mật nữa.
Và 3Ds chỉ được thực hiện qua phương thức thứ 2 tức là `Request qua trang trung gian của ngân hàng

alt text

Bắt đầu

Bước đầu tiên bạn phải có tài khoản develop do ngân hàng cung cấp bạn có thể đăng kí qua đây
Sau đó đăng nhập tại đây

Bạn có thể nhìn thấy 2 phương thức ngân hàng đang cung cấp
alt text
Bạn có thể vào xem code mẫu, hoặc tiếp tục xem mình hướng dẫn tiếp vì trong code mẫu họ chỉ đưa cho mình xem flow của một ca sử dụng thanh toán là như nào thôi, mọi người có thể tham khảo

Ở đây mình sử dụng cách request qua bên thứ 3

Đây là một phần code mẫu của ngân hàng cung cấp
alt text
Bạn có thể để ý
order.pay_method = "";
order.merchant_id = "30132";
order.service_id = "001";

3 tham số khá là quan trọng để xác định phương thức thanh toán, và thanh toán cho merchant nào

Với pay_method bạn có thể chọn một trong những loại sau

credit:クレジットカード決済
credit3d:クレジットカード決済(本人認証サービス
(3D セキュア))
unionpay:銀聯ネット決済
webcvs:Web コンビニ決済
payeasy:Pay-easy 決済
banktransfer:総合振込決済
cyberedy:楽天 Edy 決済(楽天 Edy)
mobileedy:楽天 Edy 決済(モバイル楽天 Edy)
suica:モバイル Suica 決済
webmoney:WebMoney 決済
netcash:Net Cash 決済
bitcash:BitCash 決済
prepaid:JCB PREMO 決済
docomo:ドコモ払い
auone:au かんたん決済
softbank:S!まとめて支払い
yahoowallet:Yahoo!ウォレット決済
yahoowalletdg:Yahoo!ウォレット決済(デジコン
版)
rakuten:楽天ペイ(オンライン決済)
recruit:リクルートかんたん支払い
alipay:Alipay 国際決済
paypal:Paypal 決済
netmile:ネットマイル決済
mysoftbank:ソ フトバ ンク まとめて支払い(A)
softbank2:ソフトバンクまとめて支払い(B)
saisonpoint:永久不滅ポイント
linepay:LINE Pay
tpoint:T ポイントプログラム(オンライン決済)
applepay:Apple Pay
nppostpay:NP 後払い

Và hôm nay mình sẽ hướng dẫn sử dụng credit3d ( thanh toán bằng credit có xác thực 3D sercure)

Ngôn ngữ mình sẽ sử dụng đó là PHP

Lấy onetime token

Vì một số lý do bảo mật cũng như chính sách nên không cho phép mình lưu trữ cũng như sử dụng trực tiếp thông tin thẻ để thanh toán mà phải sử dụng qua one time token ( gần giống như mã OTP mỗi khi bạn thanh toán trực tuyến nhưng cách sử dụng khác )

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0.1//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="ja">
<head>
 <script type="text/javascript" src="http://example.co.jp/token/js/com_sbps_system_token.js"></script>
 <script type="text/javascript">
 <!--
 function doSubmit(){
 // トークン生成ロジック呼び出し
 com_sbps_system.generateToken({
 merchantId : document.getElementById('merchant_id').value,
 serviceId : document.getElementById('service_id').value,
 ccNumber : document.getElementById('cc_number').value,
 ccExpiration : document.getElementById('cc_expiration').value,
 securityCode : document.getElementById('security_code').value
 }, afterGenerateToken);
 }
 var afterGenerateToken = function(response) {
 if (response.result == "OK") {
 document.getElementById('token').value = response.tokenResponse.token;
 document.getElementById('tokenKey').value = response.tokenResponse.tokenKey;
 document.getElementById('cardBrandCode').value = response.tokenResponse.cardBrandCode;
 // マスクされた番号で上書き
 document.getElementById('cc_number').value = response.tokenResponse.maskedCcNumber;
 // カード情報のクリア
 document.getElementById('cc_expiration').value = '';
 document.getElementById('security_code').value = '';
 if(window.confirm('トークン取得しました。決済を実施します。')){
 document.getElementById('frm').submit();
 } else {
 alert("キャンセルしました。");
 }
 } else {
 alert('トークン取得に失敗しました。');
 }
 }
 // -->
 </script>
</head>
<body>
 <h1>トークン生成サンプル</h1>
 <form name="frm" id="frm" method="POST" action="Confirm.jsp">
 <dl>
 <dt>マーチャント ID</dt>
 <dd><input type="text" id="merchant_id" name="merchant_id" maxlength="5" value="12345"></dd>
 <dt>サービス ID</dt>
 <dd><input type="text" id="service_id" name="service_id" maxlength="3" value="001"></dd>
 <dt>クレジットカード番号</dt>
 <dd><input type="text" id="cc_number" name="cc_number" maxlength="16" value="4111111111111111"></dd>
 <dt>クレジットカード有効期限</dt>
 <dd><input type="text" id="cc_expiration" name="cc_expiration" maxlength="6" value="202212"></dd>
 <dt>セキュリティコード</dt>
 <dd><input type="text" id="security_code" name="security_code" maxlength="4" value="1234"></dd>
 </dl>
 <dl>
 <dt>トークン</dt>
 <dd><input type="text" id="token" name="token" value="" disabled></dd>
 <dt>トークンキー</dt>
 <dd><input type="text" id="tokenKey" name="tokenKey" value="" disabled></dd>
 <dt>カード会社ブランドコード</dt>
 <dd><input type="text" id="cardBrandCode" name="cardBrandCode" value="test" disabled></dd>
 </dl>
 <table>
 <tr><td>
 <input type="button" name="btnSend" id="btnSend" value="送信" onClick="doSubmit()">
 <input type="reset" name="btnReset" id="btnReset" value="リセット">
 </td></tr>
 </table>
 </form>
</body>
</html>

<script type="text/javascript" src="http://example.co.jp/token/js/com_sbps_system_token.js"></script>

Đây là SDK cung cấp từ phía ngân hàng bạn chỉ việc làm theo mẫu trên là lấy được

Tạo data request cho form

function f_submit(resToken) {
    let free_csv = "DEALINGS_TYPE=10,TOKEN_KEY="+resToken.token_key+",TOKEN="+resToken.token;
    let free_csv_padding = paddingFreeCSV(free_csv);
    let urlSuccess = $('#url-success').val();
    let urlCancel = $('#url-cancel').val();
    let urlError = $('#url-error').val();
    let urlPagecon = $('#url-pagecon').val();
    let urlGetFreeCsv = $('#url-get-free-csv').val();
    let urlgenHashKey = $('#url-gen-hash-key').val();
     var order = new Order();
    order.pay_method            = "credit3d";
    order.merchant_id           = merchan_id;
    order.service_id            = service_id;
    order.cust_code             = member_id;
    order.sps_cust_no           = "";
    order.sps_payment_no        = "";
    order.order_id              = makeID();
    order.item_id               = resToken.item_name;
    order.pay_item_id           = "";
    order.item_name             = resToken.item_name;
    order.tax                   = "";
    order.amount                = resToken.item_name.replace(strReplace, '');
    order.pay_type              = "0";
    order.auto_charge_type      = "";
    order.service_type          = "0";
    order.div_settele           = "";
    order.last_charge_month     = "";
    order.camp_type             = "";
    order.tracking_id           = "";
    order.terminal_type         = "0";
    order.success_url           = urlSuccess;
    order.cancel_url            = urlCancel;
    order.error_url             = urlError;
    order.pagecon_url           = urlPagecon;
    order.free1                 = "";
    order.free2                 = "";
    order.free3                 = "";
    order.free_csv_input        = free_csv_padding;
    order.request_date          = getYYYYMMDDHHMMSS();
    order.limit_second          = "300";

     $.ajax({
         method: 'POST',
         data: {
             free_csv: order.free_csv_input
         },
         url: urlGetFreeCsv
     }).done((encrypt) => {
         //チェックサム
         order.free_csv = encrypt;
         $.ajax({
             method: 'POST',
             data: {
                 text: order.toString()
             },
             url: urlgenHashKey
         }).done((sps_hashcode) => {
             order.sps_hashcode = sps_hashcode;
             feppost(order);
         });
     });
}
// 日時の取得
function getYYYYMMDDHHMMSS(){
    var now = new Date();
    return now.getFullYear() + zeroPadding(now.getMonth() + 1) + zeroPadding(now.getDate()) +
           zeroPadding(now.getHours()) + zeroPadding(now.getMinutes()) + zeroPadding(now.getSeconds());
}

Bạn nên lưu ý về charset khi submit lên, vì có chưa tiếng nhật nó sẽ liên quan đến các loại full-width hoặc half-with (Mình không nhớ rõ lắm nhưng request chỉ được là half-width thôi không thì sẽ bị lỗi )

Tạo form submit

function feppost(order) {

    var connectUrl = "https://stbfep.sps-system.com/f01/FepBuyInfoReceive.do";
    var form =
        $('<form></form>',{action:connectUrl,target:'receiver',method:'POST'}).hide();

    var body = $('body');
    body.append(form);
    form.append($('<input>',{type:'hidden',name:'pay_method'        ,value:order.pay_method                  }));
    form.append($('<input>',{type:'hidden',name:'merchant_id'       ,value:order.merchant_id                 }));
    form.append($('<input>',{type:'hidden',name:'service_id'        ,value:order.service_id                  }));
    form.append($('<input>',{type:'hidden',name:'cust_code'         ,value:order.cust_code                   }));
    form.append($('<input>',{type:'hidden',name:'sps_cust_no'       ,value:order.sps_cust_no                 }));
    form.append($('<input>',{type:'hidden',name:'sps_payment_no'    ,value:order.sps_payment_no              }));
    form.append($('<input>',{type:'hidden',name:'order_id'          ,value:order.order_id                    }));
    form.append($('<input>',{type:'hidden',name:'item_id'           ,value:order.item_id                     }));
    form.append($('<input>',{type:'hidden',name:'pay_item_id'       ,value:order.pay_item_id                 }));
    form.append($('<input>',{type:'hidden',name:'item_name'         ,value:order.item_name                   }));
    form.append($('<input>',{type:'hidden',name:'tax'               ,value:order.tax                         }));
    form.append($('<input>',{type:'hidden',name:'amount'            ,value:order.amount                      }));
    form.append($('<input>',{type:'hidden',name:'pay_type'          ,value:order.pay_type                    }));
    form.append($('<input>',{type:'hidden',name:'auto_charge_type'  ,value:order.auto_charge_type            }));
    form.append($('<input>',{type:'hidden',name:'service_type'      ,value:order.service_type                }));
    form.append($('<input>',{type:'hidden',name:'div_settele'       ,value:order.div_settele                 }));
    form.append($('<input>',{type:'hidden',name:'last_charge_month' ,value:order.last_charge_month           }));
    form.append($('<input>',{type:'hidden',name:'camp_type'         ,value:order.camp_type                   }));
    form.append($('<input>',{type:'hidden',name:'tracking_id'       ,value:order.tracking_id                 }));
    form.append($('<input>',{type:'hidden',name:'terminal_type'     ,value:order.terminal_type               }));
    form.append($('<input>',{type:'hidden',name:'success_url'       ,value:order.success_url                 }));
    form.append($('<input>',{type:'hidden',name:'cancel_url'        ,value:order.cancel_url                  }));
    form.append($('<input>',{type:'hidden',name:'error_url'         ,value:order.error_url                   }));
    form.append($('<input>',{type:'hidden',name:'pagecon_url'       ,value:order.pagecon_url                 }));
    form.append($('<input>',{type:'hidden',name:'free1'             ,value:order.free1                       }));
    form.append($('<input>',{type:'hidden',name:'free2'             ,value:order.free2                       }));
    form.append($('<input>',{type:'hidden',name:'free3'             ,value:order.free3                       }));
    form.append($('<input>',{type:'hidden',name:'free_csv'          ,value:order.free_csv                    }));
    form.append($('<input>',{type:'hidden',name:'request_date'      ,value:order.request_date                }));
    form.append($('<input>',{type:'hidden',name:'limit_second'      ,value:order.limit_second                }));
    form.append($('<input>',{type:'hidden',name:'hashkey'           ,value:order.hashkey                     }));
    form.append($('<input>',{type:'hidden',name:'sps_hashcode'      ,value:order.sps_hashcode                }));

    for (i = 0; i < order.orderDetail.length; i++) {
        form.append($('<input>',{type:'hidden',name:'dtl_rowno'         ,value:order.orderDetail[i].dtl_rowno             }));
        form.append($('<input>',{type:'hidden',name:'dtl_item_id'       ,value:order.orderDetail[i].dtl_item_id           }));
        form.append($('<input>',{type:'hidden',name:'dtl_item_name'     ,value:order.orderDetail[i].dtl_item_name         }));
        form.append($('<input>',{type:'hidden',name:'dtl_item_count'    ,value:order.orderDetail[i].dtl_item_count        }));
        form.append($('<input>',{type:'hidden',name:'dtl_tax'           ,value:order.orderDetail[i].dtl_tax               }));
        form.append($('<input>',{type:'hidden',name:'dtl_amount'        ,value:order.orderDetail[i].dtl_amount            }));
        form.append($('<input>',{type:'hidden',name:'dtl_free1'         ,value:order.orderDetail[i].dtl_free1             }));
        form.append($('<input>',{type:'hidden',name:'dtl_free2'         ,value:order.orderDetail[i].dtl_free2             }));
        form.append($('<input>',{type:'hidden',name:'dtl_free3'         ,value:order.orderDetail[i].dtl_free3             }));
    }

    form.submit();
}

Quan trọng

Vì sử dụng 3Ds nên phía ngân hàng bắt buộc mình phải mã hoá một số nội dung liên quan đên thông tin tài khoản ( one time token ) bắt mình phải mã hoá dữ liệu theo thuật toán 3DES như sau:

Tạo spsHashcode

    public function generateSpsHashcode()
    {
        $data = $this->request->data;
        if (isset($data['text']) && $data['text']) {
            $text = $data['text'];
            echo $this->generateSpsHashcode($text);
        }
        die;
    }

private function generateSpsHashcode($string)
    {
        $hashKey = Configure::read('Purchase.hashKey');
        return sha1($string.$hashKey);
    }

Mã hoá free csv

public function generateFreeCsv()
    {
        $data = $this->request->data;
        if (isset($data['free_csv']) && $data['free_csv']) {
            $text = $data['free_csv'];
            echo $this->encrypt3DES($text);die;
        }
        die;
    }

private function encrypt3DES($text)
    {
        $text = $this->padding3DES($text);
        $cipher = 'DES-EDE3-CBC';
        $secretKey = Configure::read('Purchase.secretKey');
        $iv = Configure::read('Purchase.iv');
        if (in_array($cipher, openssl_get_cipher_methods()))
        {
            $ciphertext = openssl_encrypt($text, $cipher, $secretKey, $options=0, $iv);
        }
        return $ciphertext;
    }

private function padding3DES($text)
    {
        $mod = strlen($text) % 8;
        if ($mod === 0) {
            return $text;
        }
        $padding = '';
        for ($i = 0; $i < 8 - $mod; $i++) {
            $padding .= ' ';
        }
        return $text.$padding;
    }

2 route trên sẽ được gọi tại

  $.ajax({
         method: 'POST',
         data: {
             free_csv: order.free_csv_input
         },
         url: urlGetFreeCsv
     }).done((encrypt) => {
         //チェックサム
         order.free_csv = encrypt;
         $.ajax({
             method: 'POST',
             data: {
                 text: order.toString()
             },
             url: urlgenHashKey
         }).done((sps_hashcode) => {
             order.sps_hashcode = sps_hashcode;
             feppost(order);
         });
     });

Tạo callback url

Success

Là url của trang web khi thanh toán thành công sẽ được điều hướng về

Error

Là url của trang web khi xảy ra lỗi trong quá trình thanh toán

Cancel

Là url của trang web khi khách hàng huỷ bỏ thanh toán

Pagecon callback

Đây là url quan trọng để khi bạn xác thực thành công thông tin thẻ, ngân hàng sẽ request đến url này của bạn báo rằng KH có thể thanh toán được, ở đây mình phải tiến hành xử lí cho tài khoản đã thanh toán tuỳ vào nghiệp vụ của trang web, sau đó trả về OK nếu thành công hoặc NG nếu thất bại

Kết thúc

Trong bào hướng dẫn này mình chỉ nói một cách bao quát nhất và nhấn mạnh vào những lưu ý quan trọng dễ xảy ra lỗi trong quá trình tích hợp thanh toán.
Vậy là chúng ta đã tích hợp thành công thanh toán bằng softbank vào trang web của bạn.
Chúc bạn thành công

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Lưu Quốc Khánh

1 bài viết.
13 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
1 bài viết.
13 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!