LINE PAY をjavaで導入してみた。



LINE PAYとはプリペードカードのようにチャージして使う電子マネーです。

WeChatPayのようにクレカで落とすことはできませんが、本人確認必要ですが、銀行引落におるオートチャージができます。ただ対応している銀行も限られています。イメージとしてはSuicaのようなものです。

36998040

それがスマホのLINEアプリから決済できる仕組みです。

SuicaのようなICチップによる非接触ICカードは使い勝手もよいのですが、導入するのに専用な端末が必要であったりとコストがかかります。

一方でLINE PAYが提供しているQRコード決済は、QRコードやバーコードを読み込むだけでお店側が決済できるので既存の設備だけで導入が可能です。

中国ではコード決済がとても広まっており、ウィチャットペイやアリペイなどが使われているんですね。クレジット情報を隠蔽して安全に使える点とスマホだけで決済できる点がいいなーって思っています。

LINEPAYはクレカと紐づけて決済できない、プリペイド式なので使い勝手が悪いのと、オートチャージにするための銀行本人確認が面倒なことと、対応銀行が少ないことがネックです。プリペイド式を使う人って年齢層低い人ですからね。

で、導入までやりきってから、Origami Payのほうが全然よくない!?って思った次第です。きっと実装方法は同じはずだから・・・まぁいっか。

Origami Payは、クレカと紐づけて決済ができることと、alipayと提携しているのでそこもセールスポイントが高いです!

で、導入したけど、実際の店舗では日の目を見ることがなさそうなLINE PAYですが一応解説していきます。


スポンサーリンク

決済の大まかな流れ

まず、LINE PAYを利用するシーンの確認です。

今回は対面決済、ようは店頭で決済するシーンを想定します。

お店の人はレジ打ちをして金額が確定し、支払方法がLINE PAYになったら、お客さんから以下のようなQRコードをスマホ上に出して頂きます。

それをお店の人は読み取り、LINEPAYに決済していいですか?とお伺いを立てて、OKですと返事をもらえたら決済確定します。

・QRコードを読み取り、LINE PAY APIにreserve(予約)します。QRコードがトークン、予約時にLINEPAYからOKだった場合の通知先URL(confirmUrl)も指定します。

・LINE PAYはreserve情報を読み取り、OKであれば、reserve時に送ってもらったURLに対して通知します。

・店側端末は通知を読み取り、決済の確定confirmを送って決済完了となります。

※confirmUrlで通知を受け取って次のアクションに移るのが正しい処理だと思いますが、reserveしてconfirm urlに対して即時通知が来ますので、今回は、reserveして、そのままconfirmします。仮にconfirmが失敗したらリトライを何回か繰り返す。処理にしました。めどかったので。

導入マニュアル、テスト環境について

LINE PAY 導入方法がここにあります。

 

ドキュメントはこちらからダウンロードできます。

 

申請&技術サポートもこちらのメールアドレスから答えてくれます。

ドキュメントに書かれている技術サポートアドレスからは返事がなかった…です。

linepay_judge_biz_jp@linecorp.com

sandboxという開発環境は簡単に手に入ります。

WS000006

java大まかな実装方法

①webCameraでポーリングしてQRコードを認識したら読み込む処理。

②決済画面になったら、webcameraからQRコードを読み取り web apiを叩きに行く。

③reserve して 即時confirm をする。confirm失敗したらリトライすればいいんじゃない。

④transactionIDを保持して、返金時はrefundを呼び出して返金。ただし2回目は不可とする。

※LINE PAYの仕様で1つの注文に対してrefundは2回までで、2回目はrefundamount 返金金額に変わらず全額強制返金になる仕様があるため。


スポンサーリンク

Web Camera を使ったQRコード読み取り

これは巷にたくさんサンプルが溢れています。バーコードでもいいですけど。

zxingというライブラリを使用しました。

zxing-javase-3.3.0

zxing-core-3.3.0

サンプルは適当にみて、以下、大事なところ、WebcamMotionDetector でポーリング1.5秒ごとに画像に変化があれば撮影を行う。

撮影した画像ファイルをzxingで読み込んでQRコードから値を取りだす。という処理です。

WebcamMotionDetector detector = new WebcamMotionDetector(webcam);
detector.setInterval(1500);

detector.addMotionListener(new WebcamMotionListener() {

@Override
public void motionDetected(WebcamMotionEvent event) {
try {
BufferedImage image = webcam.getImage();
File y = new File(path);
ImageIO.write(image, "JPG",
new File(fPath));

// QRreader
LuminanceSource source = new BufferedImageLuminanceSource(image);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Reader reader = new MultiFormatReader();
Result decodeResult = reader.decode(bitmap);
//デコード処理
String result = decodeResult.getText();
//標準出力
qrCode = result;
System.out.format("読み取り結果=%1$s", result);
} catch (Exception e) {
e.printStackTrace();
}
}
});
detector.start();

Web CameraからQRコードを読み取り LINE PAY APIを叩く

web cameraの起動に時間がかかるので、常にポーリングして待機しているほうがいいと思いますなので、qrCode = result;でstaticフィールドに入れて、決済時に以下のような画面を出して待機中にこのフィールドに値があればLINE PAY APIを叩くようにしました。ポーリングごとにqrCode を取得した時間も保持して、5秒以上たてばリフレッシュするような仕組みにしました。

if (JasperUtil.getSecond(qrCodeTime, now()) > 5) {
qrCode = "";
}

reserve api は仕様書を見てもらって、必要なパラメータを入れていきます。

confirm url は必須項目ですが、その通知を受け取って処理することを今回省くのでなんでもいいので適当に値を入れておきます。

String url = BASE_URL + "/v2/payments/request";
try {

Map<String, String> param = new HashMap<String, String>();
param.put("productName", "クリーニング");
param.put("productImageUrl", "https://www.403.co.jp/images/logo.jpg");
param.put("amount", price + "");
param.put("currency", "JPY");

// qrCodeの値がそのままtokenです。
param.put("oneTimeKey", token);
param.put("confirmUrl", "https://www.403.co.jp/images");

// payOrderIdも任意なものでいいので、店舗側の注文番号かな。

param.put("orderId", payOrderId);

NetHttpTransport transport = new NetHttpTransport.Builder().setProxy(proxy).build();
HttpRequestFactory factory = transport
.createRequestFactory(new HttpRequestInitializer() {
public void initialize(HttpRequest request) throws IOException {
request.setConnectTimeout(JasperProperty.TIMEOUT_MILI);
request.setReadTimeout(JasperProperty.TIMEOUT_MILI);
}
});

URI uri = new URI(url);
URIBuilder _uri = new URIBuilder(uri);
String uri2 = _uri.build().toString();

String paramStr = JasperJSON.encode(param);
HttpContent content = new ByteArrayContent("application/json", paramStr.getBytes("UTF-8"));
HttpRequest request = factory.buildPostRequest(new GenericUrl(uri2), content);
HttpHeaders headers = new HttpHeaders();
headers.setContentEncoding("UTF-8");
request.setHeaders(headers);

// chanel id と secret key は管理画面から確認してね。

headers.set("X-LINE-ChannelId", getChannel());
headers.set("X-LINE-ChannelSecret", getSecretKey());
request.setConnectTimeout(JasperProperty.TIMEOUT_MILI);
request.setReadTimeout(JasperProperty.TIMEOUT_MILI);

HttpResponse response = request.execute();
BufferedReader br = new BufferedReader(
new InputStreamReader(response.getContent(), "UTF-8"));
String output = br.readLine();
Map<String, Object> map = JasperJSON.decode(output);
if (map.get("returnCode").equals("0000")) {
Object b = ((Map) map.get("info")).get("transactionId");
String s = b.toString();
return s;
}
return map.get("returnCode").toString();
} catch (Exception e) {
e.printStackTrace();
}
return "9999";

という感じです。channel id secret key は管理画面から。

WS000004

また本番環境は固定IPからしか叩けないのでプロキシサーバー経由でAPIを叩いています。WS000005WS000003

プロキシサーバーは月額500円でaws light sailで出来るからこちらも参考に。

ほぼ同じで、confirm apiを叩きます。その際にtransaction id が必要になります。

reserve api の戻り値で取得できるのでそれを使います。

String url = BASE_URL + "/v2/payments/" + transactionId + "/confirm";
try {

Map<String, String> param = new HashMap<String, String>();
param.put("amount", price + "");
param.put("currency", "JPY");

・・・・以下同じ

reserve して 即時confirm をする。confirm失敗したらリトライ

で、まとめると こんな感じになります。

public String charge(String qrCode, String payOrderId, int price) {
String transaction = reserve(qrCode, price, payOrderId);
if (transaction.length() == 4) {
return transaction;
}
String result = "";
// return transaction id を返却
for (int i = 0; i < 3; i++) {
result = confirm(transaction, price);
try {
Thread.sleep(1000);
} catch (Exception e) {
// TODO: handle exception
}
if (result.length() > 4) {
return result;
}
}
return result;
}


スポンサーリンク

戻り値のTransactionIdを保持しておき、返金時に使う。

これも上記とほぼ同じですが、

String url = BASE_URL + "/v2/payments/" + transaction + "/refund";
try {

Map<String, String> param = new HashMap<String, String>();
param.put("refundAmount", price + "");

・・・以下同様

 

です。

参考になりましたでしょうか。

今のコンビニを見るとauウォレットからなんやらかんやらすごい決済方法に対応していますよね。ごちゃごちゃして 覚えるの大変そう・・・(´◉◞౪◟◉)

ということで、ここまでやっておいて、line payは様子見かな。

やるなら origami pay か 楽天ペイが良さそう。

両社とも組み込みAPIの公開はしていなくて、あくまでもipadとかをつかってそれでやって形式のいけてないモデル。 ふぁぁぁ。。。

No. 2711

スポンサーリンク



最高級 宅配クリーニングのネットで洗濯.com
クリーニング403が安心・高品質な本物のクリーニングをお届けします。

キャンプ場からそのまま送れるテントクリーニング.com
テント・タープなどのクリーニングと合わせて撥水加工、UV加工がオススメ!