まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

タグ:nodejs

前回の続きです。今回は前回紹介した PayPay API(SDK) を使って決済・確認・返金に対応可能なアプリケーションのサンプルを作りましたので、その使い方を紹介します。 またこのサンプルアプリケーションには実際の決済を行うための加盟店登録(次回紹介予定)時の審査に必要な使い方ページや、特定商取引法に基づく表記のページも含めています。そのため、売りたいものやサービスが既にある場合は商品を自分のものに入れ替え、使い方ページや特定商取引法に基づく表記のページを自分のものに書き換えることで審査に使うこともできると思っています。


【サンプルアプリケーションを動かすための準備】
PayPay 決済のサンプルアプリケーションを手元で動かす上で大きく3つ、前提条件となる準備作業が必要です。

(1)Node.js
以下で紹介するサンプルアプリケーションは Node.js を使って実装されています(もう少し詳しく言うと Node.js + Express + EJS を使って実装したサーバーサイドアプリケーションで、jQuery や Bootstrap を使って UI を作っています。まあ昔ながらのシンプルな構成になっていると思ってください)。

このサンプルアプリケーションを手元の PC で動かすには Node.js がインストールされている必要があります(他のライブラリなどはサンプルアプリケーション側に用意されています)。既にインストール済みであれば先に進んでください。インストールできていない場合は公式サイトを参照し、自分の環境向けの Node.js をインストールしておいてください。

(2)PostgreSQL
前回のブログエントリでも紹介しましたが、サンプルアプリケーションは(返金などに対応するため)決済トランザクションをデータベースに記録します。そのデータベースが必要なんですが、本サンプルアプリケーションでは PostgreSQL を使う想定で記述されています。個人的にはデータベースは Docker 環境で用意するのがいいと思っていますが、なんらかの方法で PostgreSQL にアクセスできるような環境を用意しておいてください。

ちなみに Docker がインストール済みであれば以下のコマンドで PostgreSQL データベース(データベース名は mydb 、コンテナ名は mypostgres)を動かすことができます:
$ docker run -d --name mypostgres -p 5432:5432 -e POSTGRES_USER=admin -e POSTGRES_PASSWORD=password -e POSTGRES_DB=mydb postgres

また以下のコマンドを追加で実行して、 mydb にトランザクション記録用テーブルである transactions を定義します:
$ docker container exec -it mypostgres psql -h localhost -U admin -d mydb

mydb=# create table if not exists transactions ( id varchar(100) not null primary key, order_id varchar(50) not null, amount int default 0, currency varchar(10) default '', created bigint default 0 );

mydb=# \q

このような PostgreSQL データベースと transactions テーブルが用意されている前提で以下を説明します。


(3)PayPay アプリ
またこのサンプルアプリケーションを使って(テスト環境で)商品を購入したり、購入した商品の返金対応をしたりするのですが、そのためにはスマホにインストールされた PayPay アプリが必要です。普段自分が使っている PayPay アプリでもできないことはないのですが、(環境切替が面倒だと思うので)できればアプリケーション試験用に普段使っているものとは別のスマホに PayPay アプリをインストールしておくのがいいと思います。



【サンプルアプリケーションを動かしてみる】
サンプルアプリケーションのソースコードはこちらから取得できます(zip でダウンロード&展開、または git clone):
https://github.com/dotnsf/paypayapi-sample

ソースコード展開後、とりあえず初回だけ必要なライブラリのインストール作業を済ませておきます:
$ cd paypayapi-sample

$ npm install

(正しく実行できると node_modules というフォルダが作られます)

次に動作設定ファイル .env を編集します。適当なエディタ(メモ帳でも可)で .env というファイルを開いて、以下の内容を入力して保存します:
DATABASE_URL=postgres://admin:password@localhost:5432/mydb
PGSSLMODE=disable
ADMIN_ID=user
ADMIN_PW=pass
PAYPAY_API_KEY=(PAYPAY API キーの値)
PAYPAY_API_SECRET=(PAYPAY API シークレットの値)
PAYPAY_MERCHANT_ID=(PAYPAY 加盟店 ID の値)
PAYPAY_REDIRECT_URL=http://localhost:8080/paypay/redirect

DATABASE_URL は前提準備で作った PostgreSQL データベースへの URL 、ADMIN_USER と ADMIN_PASS は管理者用画面にアクセスする際の ID とパスワード(この例だと ID が user で、パスワードは pass)を指定します。

そして PAYPAY_API_KEY と PAYPAY_API_SECRET は初回 PayPay for Developers に申し込んで取得した API キーとシークレットの値、PAYPAY_MERCHANT_ID は加盟店 ID です。それぞれ自分の環境で調べた値を指定してください(自分で取得した値を入れないと意味なくなります):
2025052910


最後に(これは編集せずにそのままでも動きますが)このサンプルアプリケーションで扱う商品の一覧ファイル items.json を必要に応じて編集します:
[
  {
    "name": "商品A",
    "imageurl": "/item-a.png",
    "amount": 500,
    "currency": "JPY"
  },
  {
    "name": "商品B",
    "imageurl": "/item-b.png",
    "amount": 1000,
    "currency": "JPY"
  },
  {
    "name": "商品C",
    "imageurl": "/item-c.png",
    "amount": 1200,
    "currency": "JPY"
  }
]

デフォルトの items.json では JSON 配列フォーマットで3つの商品が記載されています。各商品ごとに商品名、商品画像 URL 、価格とその通貨単位("JPY" は「日本円」です)が指定されているので、必要に応じて内容を書き換えたり、商品を追加削除してください。

ちなみに商品画像はリモートにある画像を使う場合はその URL を、手元にある画像の場合は public/ フォルダ以下にコピーしておくとサンプルのような記述が使えます。

ここまで完了していると実際にサンプルアプリケーションを動かすことができます。動かすには以下のコマンドを実行します(ちなみに実行終了は Ctrl+C です):
$ node app


実行中のサンプルアプリケーションにアクセスするには、同じ PC でブラウザを開き、http://localhost:8080/ にアクセスします。

下のような、3つ(items.json を変更した場合は変更内容によって変わります)の商品が縦に並んでいる画面になれば起動成功です。items.json で指定した商品が商品名、画像、価格が反映されて "Buy" というボタンを一緒に表示されています:
2025061301


実際に商品を購入する前に、概説ページと特定商取引法に基づく表記ページを確認します(管理者画面は後述します)。それぞれ画面上部のリンクから遷移できます:

(概説ページ)
2025061302

(特定商取引法に基づく表記ページ)
2025061303


どちらもここでは実質的に意味のないサンプルですが、実際の決済を行う際に必要な加盟店登録(次のブログで紹介予定)時には必要なページ(のサンプル)です。加盟店登録を行う際は同様のページを自分のショップ向けにカスタマイズして用意する必要があることを意識して内容を理解してください。


【テストアカウントで PayPay アプリにログイン】
改めて商品ページに戻り、今度はどれか1つを実際に購入してみましょう。そのために購入時に備えてスマホの PayPay アプリにログインしておきます。 ただ通常の(自分が普段使っている)PayPay アカウントでログインするのではなく、PayPay for Developers でテスト用に提供されているアカウントで(仮想のお金で)ログインします。 そのための手順を紹介しますが、可能であれば普段自分が使っているスマホではなく、(持っていればですが)別のスマホで以下の手順を実行することをお勧めします。

まず PayPay for Developers にログインし、ダッシュボードのテストユーザータブを開きます。すると3つのテストアカウントがあるので、どれか一つのユーザーネームとパスワード(下図では見えていませんが、目のアイコンをクリックすると表示されます)を確認しておきます(残金が少ない場合はこの画面からチャージもできます):
2025052909



このテストユーザーを使って PayPay アプリに Developer アカウントでログインします。Developer アプリにログインするにはアプリケーションを開いて(ログイン中の場合はログアウトして)、ログイン/新規登録画面内 PayPay ロゴの上部を7回連続でタップします:
2025061305


(途中からカウントダウンが表示されます)
2025061306


7回タップすると以下のような画面になります。「PayPay for Developers のアカウントでログイン」をクリックします:
2025061307


すると(テストモードでの)ログイン画面に切り替わります。ここで先ほど確認したテストユーザーの ID(電話番号)を入力します:
2025061308


パスワードも該当する電話番号のパスワードを入力してログインします:
2025061309


ログイン後の画面はスクリーンショットが撮影できないため文章のみでの説明になります。Developer モードでのログインができたことを確認してください。


【テストアカウントの PayPay アプリで決済】
ではテストアカウントの PayPay アプリで、サンプルアプリケーションの商品を購入してみましょう。

改めて購入する商品(下図の例では「商品A」)のアクション列にある "Buy" ボタンをクリックします:
2025061301


確認メッセージが表示されるので、間違いでなければ "OK" ボタンをクリック
2025061301


すると PayPay の QR コード画面に転送されます(ここでは前回紹介した内容の「ユースケース1 ウェブ決済」機能を使っています)。この QR コードを PayPay アプリで「スキャンして支払い」します:
2025061302


アプリ側で支払いするとサンプルアプリの画面も支払い完了時の画面に切り替わり、、、
2025061303


しばらくすると元の画面に再転送されます。これで決済できています。
2025061304


始めてやってみると不思議に感じるかもしれませんが、これで支払いは(今回の場合はテストユーザー用の仮想のお金で)完了しています。 他の商品も含めて何度か決済してみてください。


【管理画面と返金対応】
ユーザーの決済までの手順は(普段使っている PayPay アプリでの決済手順と比べてもほぼ同じだと思うので)分かりやすかったのではないかと思いました。これで支払いが完了して、(このサンプルアプリの場合は)そのトランザクションが記録されています。

最後に管理者としての管理画面にもアクセスしてみます。トップ画面右上の「管理画面」リンクをクリックします(または http://localhost:8080/admin にアクセスします):
2025061305


管理者画面へアクセスするためのユーザー名とパスワードの入力を求められるので、自分で .env に設定した内容(ADMIN_USER と ADMIN_PASS の内容)を入力してログインします:
2025061306


管理画面へのログインに成功すると以下のような画面になり、過去のトランザクション(取引)一覧(下図では1件だけ)が表示されます。トランザクションの詳細を表示するには該当レコードの "info" ボタンをクリックします:
2025061401


すると以下のようなダイアログが表示され、トランザクションの現在の状態が表示されます(日付や決済額などの情報が確認できます)。トランザクションのステータスはダイアログの最上部に表示されます(下図では "COMPLETED" になっていて、決済が完了していることを意味しています)。なお、ここでは前回紹介した内容の「ユースケース2 ウェブ決済状況確認」機能を使っています:
2025061402



最後に返金対応をシミュレートしてみましょう。もしこのトランザクションを実行したユーザー(お客様)から、「クーリングオフを理由に返金してほしい」という要望があった場合に(法律的にはクーリングオフは消費者の権利なので条件を満たしている限り対応しないわけにはいかないのですが)具体的に返金するにはどうすればよいか、というシミュレーションです。

返金対応をする上で重要なのは「どの取引に対して返金処理をすればよいのか?」を明確にするということです。トランザクションの履歴は管理画面に残っていることが確認できるのですが、この一覧の中のどのトランザクションに対して返金対応すればよいか、を正確に知る必要があるのです。

そのためにはまず利用者(お客様)から取引番号を伝えてもらう必要があります。以下の手順を利用者に伝えた上で、「返金を希望する PayPay 決済の取引番号を調べて送ってほしい」と伝えます。

利用者からみた取引番号の調べ方は以下の通りです。なお、PayPay アプリのスクショは撮れないようになっているので、以下は PayPay アプリの操作の様子を別のスマホで撮影した写真を添付します。またこの PayPay アプリは PayPay for Developers のテスト用アカウントでログインしているので、その旨のメッセージが画面上部に表示されています。


まず利用者に PayPay アプリにログインした状態で開いてもらいます:
2025061403


ここから「取引履歴」と書かれた部分をタップして、過去の取引履歴を表示してもらいます:
2025061404


直近の取引の履歴が表示されます。ここから返金を希望する取引を選んでタップしてもらい、
2025061405


返金対象の取引の画面が表示されます。ここで更に「詳細を見る」と書かれた箇所をタップしてもらうと、
2025061406


詳細情報が展開されます。その中に「取引番号」と書かれた箇所があります。返金対応にはこの情報が必要なので、取引番号の内容を伝えてもらってください:
2025061407


返金対象の取引番号が分かったら、改めてサンプルアプリの管理画面を開き、トランザクション一覧で order id がその取引番号になっているトランザクションを見つけます(下図の例だとトランザクションが1件しかないので簡単に見つかりますが、数多くあった場合はソートなどして見つけてください)。これで返金対象トランザクションが分かりました:
2025061408


返金対象トランザクションが分かったら、このトランザクションの返金対応を実施します。なお返金対応はアンドゥができない点に注意してください。また、ここでは前回紹介した内容の「ユースケース3 返金対応」機能を使って実装しています。

対象トランザクションの右側にある "Refund" ボタンをクリックします:
2025061408


最初の確認画面が表示されます。対象トランザクションが一致しているか確認して OK をクリック:
2025061409


返金処理には必ず理由を入力する必要があります。返金理由を記載して OK をクリック:
2025061410


返金処理前の最後の確認画面が表示されます。内容を確認し、この内容での返金を実行する場合は OK をクリックします:
2025061411


実行結果が表示されます。画面内に返金理由や paymentId(order id)、返金額などの情報が表示されています:
2025061412


再度、管理画面が表示されます。ここで返金したトランザクションのステータスを再確認してみます。該当トランザクションの "info" ボタンをクリックします:
2025061413


すると(正しく返金が実行されていれば) "REFUNDED" というタイトルのダイアログが表示され、返金理由なども表示されます。無事に返金できていたようでした:
2025061314


ここまでできていれば、利用者の PayPay アプリ側にも返金されています。PayPay アプリの取引履歴を確認すると返金されていることが確認できるはずです:
2025061415


サンプルアプリケーションと PayPay アプリ(とテストユーザー)を使った購入と、購入内容の確認、そして返金対応の手順が確認できました。ここまで確認すると、返金対応するには過去の(取引番号含めた)取引履歴の情報が残っていないと難しいのでデータベースに記録する必要があった、というのもなんとなく理解いただけるのではないかと思います。


管理機能や返金処理まで実装し、更に加盟店登録時に必要な概要ページや特定商取引法に基づく表記ページまで含まれたサンプルアプリは我ながら珍しいと思うので、これを参考に(商品や見た目をカスタマイズすれば)返金機能まで対応した PayPay ショップサイトを比較的容易に作れるのではないかと思っています。よかったら是非カスタマイズして使ってください。


次回はこのシリーズの最終回の予定です。これまでは PayPay for Developers アカウントのテストユーザーを使ったアプリ動作確認を紹介してきましたが、次回は PayPay に加盟店登録を行い、本当に(テストユーザー以外でも)PayPay 決済が可能になるような手続きを紹介する予定です。













前回の続きです。今回はウェブアプリに PayPay 決済機能を実装するための API(SDK) と、この API をどう使って(次回紹介予定の)サンプルウェブアプリケーションを実装したか、を紹介します。

前回紹介した PayPay for Developers にアカウント登録をすることで PayPay API を使ったアプリケーション開発の準備ができました。この後、実際に動くサンプルアプリケーションも紹介予定ですが、そのサンプルアプリケーションの中で使っている PayPay 決済機能がどのような API を使ってどのように実装されているか、を紹介します。


【PayPay 公式 API】
まず PayPay が公開している公式 API であるインテグレーション SDK はこちらです:
https://developer.paypay.ne.jp/products/docs

同ページ内で様々な SDK が紹介されていますが、特にウェブアプリケーションの中で決済を実施するためのウェブペイメント SDK についての公式資料はこちらです:
https://developer.paypay.ne.jp/products/docs/webpayment

このブログシリーズの次回でサンプルアプリケーションを紹介しますが、そのサンプルアプリケーションは Node.js を使って実装しています。この Node.js 用 SDK の公式資料はこちらです(本ブログエントリで紹介する内容はこのページで紹介されているものの抜粋です):
https://www.npmjs.com/package/@paypayopa/paypayopa-sdk-node/v/0.5.1


自作サービスで PayPay を有効にするには上記の API(SDK) を使って決済したり、決済状況を確認したり、といった機能をアプリケーション内で実装する必要があります。以下では利用場面別に3つのユースケースと、そのための API(SDK) 実装方法を紹介します。


【データベースの用意】
まず最初にデータベースを用意します。詳しくは後述しますが、PayPay は単に支払いに使うだけなら決済情報を記録する必要はないのですが、実際に運用する場合は決済情報を記録しておかないと(返金リクエストなどの)各種問い合わせに対応できなくなってしまいます。

以下の説明ではこのような定義の transactions テーブルが作成されていることを前提に説明しています。もちろん利用目的によってはユーザーの情報などを加えてもいいと思いますが、最低限このくらいの情報を記録しておく必要がある、と思ってください:
create table if not exists transactions ( id varchar(100) not null primary key, order_id varchar(50) not null, amount int default 0, currency varchar(10) default '', created bigint default 0 );

この transactions テーブル内の各列には以下のような情報を格納します:
列名格納する値
idID(merchantPaymentId)
order_idオーダー ID(利用者からは「取引番号」として見える値)
amount取引額
currency取引通貨
created作成日時のタイムスタンプ



【SDK の初期化】
次に以下で3つ紹介するユースケースの全てで共通しているのですが、Node.js 用 PayPay SDK を使う場合、各処理を実行する前に SDK を初期化しておく必要があり、この初期化の際には以下3つの値が必要です:
・API キー
・API シークレット
・加盟店 ID

これらは PayPay for Developers にログインした直後のダッシュボード画面に表示されているものを使います(他の人から教えてもらったり、教えたりするものではないので注意してください):
2025052910


これらをの値を使って、以下のコードを実行して初期化を実行しておきます:
var PAYPAY = require( "@paypayopa/paypayopa-sdk-node" );
var PAYPAY_API_KEY = '(API キー)';
var PAYPAY_API_SECRET = '(API シークレット)';
var PAYPAY_MERCHANT_ID = '(加盟店 ID)';

PAYPAY.Configure({
  clientId: PAYPAY_API_KEY,
  clientSecret: PAYPAY_API_SECRET,
  merchantId: PAYPAY_MERCHANT_ID,
  productionMode: false  //. テスト環境の場合は false 、本番の場合は true
});

以下3つのユースケース全てはここまで完了している前提で実行してください。


【ユースケース1 ウェブ決済】
ある意味最もシンプルな「PayPay アプリで支払いを行う」場面を想定した機能を作る部分です。支払いを行う利用者の立場で考えると「QR コードを読み取って支払う」という1ステップだけの機能だけのように感じられるかもしれませんが、ここでは以下のような3ステップを実行します:

(1)支払い内容に応じた QR コード(の URL )を作る
(2)(1)で作った URL にユーザーを転送させて QR コード決済してもらう
(3)決済内容を(返金対応ができるように)記録する

(1)ではサーバーサイドで以下のようなコードを実行します(◎◎シャンプーという 1000 円の商品を購入した例での内容です):
var { v4: uuidv4 } = require( 'uuid' );  //. UUID 作成用
  :
  :

    var payload = {
      merchantPaymentId: 'paypayapi-sample' + uuidv4(),  //. ユニークID
      amount: { amount: '1000', currency: 'JPY' },  //. 1000円
      codeType: "ORDER_QR",
      orderDescription: '◎◎シャンプー',
      isAuthorization: false,
      redirectUrl: 'http://localhost:8080/paypay/redirect',  //. リダイレクト先
      redirectType: "WEB_LINK",
      userAgent: 'PayPayAPI Sample App/0.0.1'
    };

    //. 決済用 QR コードページを生成
    PAYPAY.QRCodeCreate( payload, function( response ){
      if( response.STATUS == 201 ){
        //. 決済内容をセッションに記録
        req.session.qr_data = response.BODY.data;

        //. 生成された QR コードページにリダイレクト
        res.redirect( response.BODY.data.url );
      }


ここでは QR コードページを生成するために PAYPAY.QRCodeCreate という関数を実行するのですが、その関数に渡すパラメータオブジェクトである payload という変数を定義します。ここでは merchantPaymentId にユニークな ID を生成して入れておきます。また amount には決済額の情報を数値と通貨単位に分けて入力し、orderDescription に決済内容を示す文字列(商品名など)、そして redirectUrl に(3)での処理を想定した QR コード決済が完了した後に戻ってくるページ(リダイレクト先)を指定します。また codeType には "ORDER_QR" を、redirectType には "WEB_LINK" をそれぞれ指定します。

このようにして作成した payload を引数指定して PAYPAY.QRCodeCreate を実行します。そして生成が成功したこと(response.STATUS が 201 になっていること)を確認後に(決済後のリダイレクト先で再度この時の情報にアクセスできるように)決済内容をセッションに記録した上で生成された QR コードページに利用者をリダイレクトする、という処理を実行しています。

(2)利用者は(1)の最後のリダイレクト先の QR コードを PAYPAY アプリで読み取って決済します。決済が完了すると利用者は(1)の payload.redirectUrl で指定したリダイレクト先に転送されます。

(3)利用者の決済が完了した後に転送された後の処理です。この時点で決済は完了しているので、支払い自体は済んでいるとも言えます。ただここで決済の情報を記録しておかないと、誰がいつどんな決済を行ったのか、という情報は PayPay for Developers のダッシュボード画面を見ないとわからなくなってしまいますし、あるユーザーから(クーリングオフなどの理由で)返金を求められた場合、いつのどの決済に対する返金対応なのかもわからなくなってしまいます。その対応のためにも完了した決済内容はデータベースなどに記録しておくことが必要だと思っています((1)で決済直前に決済内容をセッションに記録していたのはそのためです)。

そのための実装は以下のようになります:
//. 決済完了後のリダイレクト先での処理
app.get( '/paypay/redirect', async function( req, res ){
  //. (1)で記憶させた内容をセッションから取り出し
  var qr_data = req.session.qr_data;

if( qr_data.merchantPaymentId ){ //. merchantPaymentId を使ってオーダー ID を取り出す var order_id = ''; var response = await PAYPAY.GetCodePaymentDetails( Array( qr_data.merchantPaymentId ) ); if( response.STATUS == 201 ){ var body = JSON.parse( response.BODY ); //. body.data.paymentId に格納されたオーダー ID を取り出す if( body && body.data && body.data.paymentId ){ order_id = body.data.paymentId; } } //. qr_data.merchantPaymentId, order_id, qr_data.amount.amount, qr_data.amount.currency をデータベースに記録する : :

まず(1)で指定したリダイレクト先 URL である '/paypay/redirect' での処理内容として以下を記述します。最初に(1)でセッションに記憶させた情報を取り出し、(1)実行時に生成したユニーク ID である merchantPaymentId を引数に PAYPAY.GetCodePaymentDetails 関数を実行して決済結果の状況を取り出します。

データベースに記録しておきたい情報は「利用者側からの決済情報 ID として確認できる オーダー ID 」の値で、利用者の PAYPAY 画面からは取引履歴内の「詳細を見る」という部分を展開した先の「取引番号」として表示されているものです:
2025060901


※もし利用者から返金対応を求められた場合は、利用者にこの画面を出してもらい、「取引番号を教えてください」と伝えて教えてもらうことで、サービス運用側からもどの時の決済に対する返金対応なのか、を特定することができるようになります。そのためにも(3)の処理の一部として、決済情報をデータベースに記録しておくことが必要です。


このオーダー ID の値は PAYPAY.GetCodePaymentDetails 実行後の body.data.paymentId に格納されているので、この値を取り出します。

最終的には
・実行時に生成したユニーク ID (merchantPaymentId)
・利用者側からの決済情報として確認できるオーダー ID(order_id)
・決済額と決済通貨単位(amount, currency)
・その他、日付情報など
を別途データベースに格納しておくことで、ユーザーからの決済に関する問い合わせにも対応ができるようになる、というものです。

ユースケース1においては単なる支払いだけなら(1)と(2)の処理だけ実装すればできますが、実際に運用することまで考慮すると(3)の処理も事実上必須、だと思っています。


【ユースケース2 ウェブ決済状況確認】
「PayPay アプリで支払い」の機能さえあればよいかというと、そうはいきません。そもそも支払いは完了したのかしていないのか、支払いを行った結果どうなったか、返金対応は完了しているのか、といった状況を確認できるようにしておく必要もあります。後述の返金対応をする上でも確実に支払われたと分かっている取引だけに対応する必要があるため、支払い結果を確認することができる機能も必要です。

この機能はユースケース1の(3)でも利用した PAYPAY.GetCodePaymentDetails 関数を使って実装します。状況を確認したい取引の merchantPaymentId を指定して、以下のように実行します:
PAYPAY.GetCodePaymentDetails( Array( merchantPaymentId ), function( response ){
  if( response.STATUS == 201 ){
    //. 取引情報の取得に成功
      :
      :


なお、取引情報のステータスを示す値は response.BODY.data.status に文字列として格納されています。取り得る値とそのステータスは以下のようになります(ウェブペイメントで目にする頻度が高いのは緑のステータスです):
ステータス
CREATED受付済み(QRコード作成済み)
AUTHORIZED残高ブロック済み
REAUTHORIZED増額キャプチャ時のユーザー承認後処理中
COMPLETED取引完了(支払い済み)
REFUNDED返金済み
FAILED取引失敗
CANCELED残高ブロック中止(キャンセル
EXPIRED残高ブロック期限切れ


この関数によって、merchantPaymentId が分かっていればその取引の決済状況を確認することができるようになります。


【ユースケース3 返金対応】
もう1点、上述でも何度か触れていますが、返金にも対応できるようにしておく必要があります。PayPay で決済可能なサービスを作っている以上、クーリングオフなど購入者側の事情による返金を考慮しないわけにはいきません。「返金には対応しない」わけにはいかない以上、返金要望にも対応できるようなアプリケーションにしておく必要がある、というものです。

利用者からの返金リクエストを受ける場合、まず利用者に対象取引の「取引番号」を調べてもらう必要があります。取引番号は PAYPAY アプリの取引履歴から返金対象の決済を選んだあとに「詳細を見る」を選択後に表示されます。まずはこの取引番号を利用者から教えてもらいます:
2025060901


取引番号(データベース上ではオーダー ID)がわかったら、その取引番号をオーダー ID として持つレコードを検索します(上述の transactions テーブルを使い、該当するオーダー ID を持つレコードの id(=merchantPaymentId), amount, currency を検索します。

返金対応したい決済の内容が分かったら、返金理由などと併せて PAYPAY.PaymentRefund 関数を実行して返金処理を実施します(以下のコードはデータベースに PostgreSQL を使う想定で記述されています):
var PG = require( 'pg' );
var database_url = 'posgtres://user:pass@server:5432/db';
var pg = new PG.pool( { connectionString: database_url } );
var conn = await pg.connect();

var order_id = '(返金対応したいレコードの order_id)';

//. order_id に対応する id や返金額をデータベースから取り出す
var sql = "select id, amount, currency from transactions where order_id = $1";
var query = { text: sql, values: [ order_id ] };

var result = await conn.query( query );
var id = result.rows[0].id;  //. 返金対応したいレコードの id
var amount = parseInt( result.rows[0].amount );
var currency = result.rows[0].currency;

//. 該当レコードの決済から返金する
var payload = {
  merchantRefundId: id + '-refund',
  paymentId: order_id,
  amount: { amount: amount, currency: currency },
  reason: 'クーリングオフ'  //. 返金理由
};
PAYPAY.PaymentRefund( payload, function( response ){
  if( resopnse.STATUS == 201 ){
    //. 返金成功
      :
      :


以上、今回は Node.js 用の PAYPAY API(SDK) を使って PayPay での決済や状況確認、返金処理といった実践的な処理を実装するための前提条件や方法をざっと紹介しました。

次回は今回紹介した内容を使って作成したサンプルアプリケーションをテスト環境で動かし、PayPay アプリでの決済や返金処理といった内容を実際に体験する予定です。



PayPay 、便利ですよね。具体的な数字根拠があるわけではないのですが、数ある QR コード決済システムの中でも頭一つ抜き出て利用されている印象を持っています。

自分は個人でウェブサービスを開発したり、その中のいくつかはクラウドなどを使って公開していたりするものもあります。そんな自分が長い間悩んでいたものの1つが「有償化」というものです。作ったサービスは基本的には無料で使ってもらうことを想定しているのですが、一部の特定機能については有償化できることならしたい、というものもあります(商用利用とか)。ただその上で大きな壁となるのが「どうやって料金を支払ってもらうか」でした。クレジットカード決済するような機能を個人で作れるわけではなく、PayPal のような仕組みもありましたが日本ではあまり広まっていなかったし、かといって銀行振込は手間がかかりすぎてせっかく興味を持ってくれた人の興味を失ってしまう要因にもなると思っていました。

そんな中、この PayPay は API(SDK) が提供されていて、自作ウェブアプリケーションに組み込むことができます。例えば自作アプリケーションの中で EC 機能を実装してその決済を PayPay で行うことができるようになったり、自作ウェブサービスの有料機能の支払いを PayPay で行わせることもできるようになります。日本国内での利用者も多く、この PayPay API を使うことで技術的にも比較的低い敷居でサービスの有償化を実現できるのではないかと考えています。


実は数年前に同じような内容を自分のブログ内で紹介する機会がありました。当時よりも API そのものが進化していたりすることもあるのですが、当時紹介した内容は「単なるウェブ決済」の話になってしまっていて、(例えば返金対応のことが考えられていなかったりして)非実践的な内容でした。 そんな反省も踏まえ、改めて 2025 年6月時点の情報として Node.js のサンプルも含めた PayPay API 対応ウェブアプリの実装方法を紹介したいと思います。

内容は数回に分けて紹介する予定です。初回である今回は PayPay API を使う上で必要な PayPay for Developers へのアカウント登録方法を紹介します。


【PayPay for Developers へアカウント登録する】
PayPay API を使うにはまず PayPay for Developers にアカウントを登録して、API キーなどの利用情報を取得する必要があります。そのアカウント登録手順を紹介します。 なおアカウントの作成自体は無料ですが、有効なメールアドレスが必要です。加えてテストモードでの動作確認を行うために自分が普段 PayPay アプリを使っているスマホとは異なる、別のスマホが1台あると(面倒なアカウントの切り替えが不要なので)便利です。

では PayPay for Developershttps://developer.paypay.ne.jp/account/signup ) にウェブブラウザでアクセスして、アカウントの ID となるメールアドレスと、アカウント利用時のパスワードを入力して「アカウントを作成する」ボタンをクリックします:
2025052901


すると以下のような画面に切り替わります。この時点で入力したメールアドレスに PayPay からワンタイムパスワードが記載されたメールが送信されています(この画面に戻ってくるので、この画面は消さずに以下の作業を実施してください):
2025052902


送信された対象のメールを探して、その本文内に記載されているワンタイムパスワードを確認します:
2025052903


元の画面に戻り、ワンタイムパスワードを入力して「確認する」ボタンをクリックします:
2025052904


実はアカウントの登録自体はこれだけです。そのまま次に決済時のサービス(利用者の PayPay 支払い時に表示される内容)のサービス名とサービスロゴを登録します。試験的に使うだけなら(どうせ使うのは自分だけなので)適当な名前とロゴ画像でいいと思います。実際に公開してお客様の決済に使う場合はちゃんとした(笑)名前とロゴを指定してください:
2025052905


次は決済方法の指定です。ここで紹介するのは「ウェブペイメント」だけなのですが、後で色んな機能を使いたくなる可能性もあるので、一応全部チェックして、最後に「登録する」ボタンをクリック:
2025052906


このような画面になってアカウントとショップの登録は完了です。「開始する」ボタンをクリックして PayPay for Developers にログインします:
2025052907


なお次回からはこちらの URL( https://developer.paypay.ne.jp/account/signin )でログイン画面を開き、メールアドレスと登録したパスワードを入力することでログインできます:
2025060401



【PayPay for Developers で API 利用情報やテストユーザーを確認する】
PayPay for Developers にログインすると以下のような画面になります。まずは左メニューでダッシュボードを開き、「API キー」タブを選択します。すると API キーAPI シークレット、そして画面上部に加盟店 ID が確認できると思います。PayPay API を利用するにはこれら3つの値が必要になるので、これらの値をメモするなどして、すぐに参照できるようにしておいてください:
2025052910


なお、アクセスするタイミングによってはまだこれらの情報が発行されていないか、画面に反映されていなかったりして、表示されないことがあります(下図参照)。 このような場合は一度ログアウトし、少し待ってから再ログイン後に確認してください:
2025052908


また「テストユーザー」タブを開くと、動作確認用のユーザー IDとパスワード、そして各ユーザー ID にチャージされた額(仮想のお金)が表示されます。チャージ額はこの画面から追加することもできます。またパスワードはこの画面では表示されていませんが、各パスワードの右側の「目に斜め線が引かれたアイコン」をクリックすると実際のパスワード文字列を確認することができます:
2025052909


このテストユーザーの ID とパスワードはサンプルアプリの動作を確認する時に(本当のお金の取引を伴うモードへ移行する前の、テスト環境での動作確認時に)使う予定です。


これで PayPay API を使う上での準備として必須だった PayPay for Developers へのアカウント登録は完了しました。一応触れておくと、この時点でできることは「PayPay API をテスト環境で使う」ことだけで、本当にお金の流れが発生するような実環境決済はできません。そのためには加盟店登録が必要になるのですが、その辺りはまた別の機会に紹介したいと考えています。

次回は今回用意した PayPay for Developers アカウントで PayPay 加盟店に登録する手順を紹介する予定です。










こんなツールが私以外に需要あるかどうかわからなかったのですが、作った本人である自分は結構使う機会があるので公開することにします。

ツールの用途を一言でいうと「エクセルファイル(xls, xlsx)のテキストビューワ」です。ターミナルやコマンドプロンプトで作業している時に、ふと「このエクセルファイルの中身なんだっけ?」と確認したくなることがあります。多くは業務に必要な情報がエクセルファイルの中に格納されているケースで、「エクセルを起動して編集したいわけじゃないけど、ちょっと内容を確認したい」という場合がほとんどです。あと人によってはエクセルをインストールしているとも限らないので、LibreOffice などの代替製品をインストールしていたりするんだけど、起動に時間がかかって面倒だったり・・・ 理由は様々あると思いますが、「ちょっとした理由でエクセルファイルの中身だけ確認したい」けど「ターミナル作業中にアプリケーションを切り替えるのが面倒」といった、自分にはよくあるケースで役立つと考えています。

ツールそのものはこちらからダウンロードできるようにしておきました。自作の Node.js アプリですが、先日このブログでも紹介した pkg を使って単体で実行可能なバイナリ化しました(Linux, Windows, MacOS 用。ただし MacOS 用は未検証です)。リンク先から自分の PC プラットフォームに合うものを選んでダウンロードしてください。ダウンロード後は PATH の通ったフォルダに "xls-viewer" という名前で(Windows の場合は "xls-viewer.exe" という名前で)保存しておくとコマンドプロンプトやターミナルから直接実行できるようになります。この名前で保存しておくことで、どのプラットフォーム向けであっても "xls-viewer" というツール名で実行できるようになり、以下ではこの前提で説明を続けます(ファイル名を変えずに保存した場合は自分のプラットフォーム向けのファイル名に読み替えてください)。


【エクセルファイルのサンプル】
以下でその使い方を紹介しますが、このサンプル・エクセルファイルを使った例を紹介します。こちらも併せてダウンロードしておいてください:
https://github.com/dotnsf/xls-viewer/raw/main/sample01.xlsx


ちなみにこのサンプル・エクセルファイル sample01.xlsx の中身をエクセルそのもので開くと以下のようなものです。2つのシート Sheet1 と Sheet2(2は全角文字)から構成されています。Sheet1 は(実は1箇所だけ式を使ってますが)特に文字装飾要素も使わない、ごくシンプルなシートです:
2024072301



一方 Sheet2 は式も使っていますし、日本語や文字装飾も併用しています。またわざと空の行や空の列も含めています。このエクセルファイルをサンプルとして xls-viewer を使う例を以下で紹介します:
2024072302


【xls-viewer の使い方】
以下で xls-viewer の使いかたを紹介します。準備として xls-viewer(ファイル名を変えていない場合は元のファイル名に読みかえてください)を PATH の通ったフォルダ(/usr/local/bin など)に配置してください。そしてコマンドプロンプトやターミナルを開き、サンプルエクセルファイル sample01.xlsx のあるフォルダに移動しておきます。

まずは単純に(オプションなしで)実行して見ます。xls-viewer に続いて対象のエクセルファイル名(今回の場合は sample01.xlsx)をフルパスまたは相対パスで指定して実行します。今回は実行時のパスに sample01.xlsx が存在している想定なので、パス指定なしでも実行できるはずです。成功すると以下のような表示になります(見ている人の環境によっては変な所で改行されているように見えるかもしれませんが、実際には必要な半角スペースのパディングなども加えて正しく改行されている、はず)。各シートから表示されているテキスト情報を抜き出して表示しています:
$ xls-viewer sample01.xlsx
Sheet1 :
+----+------+------+---+--+-----+
| 1  | 3    |      |   |  |     |
+----+------+------+---+--+-----+
| 3  | 2    | safa |   |  | saf |
+----+------+------+---+--+-----+
| 2  | sa   |      |   |  |     |
+----+------+------+---+--+-----+
| 43 | safd |      |   |  |     |
+----+------+------+---+--+-----+
|    |      |      | 5 |  |     |
+----+------+------+---+--+-----+

Sheet2 :
+----------------------+--+----------------------+--+------------+
| 少し長めのテキストを |  |                      |  | 太字       |
| 入力                 |  |                      |  |            |
+----------------------+--+----------------------+--+------------+
|                      |  | 更に123を追加したテ  |  | Italic     |
|                      |  | キスト               |  |            |
+----------------------+--+----------------------+--+------------+
|                      |  |                      |  | 色付き     |
+----------------------+--+----------------------+--+------------+
| aaa                  |  | 3                    |  | 下線       |
+----------------------+--+----------------------+--+------------+
|                      |  | 6                    |  |            |
+----------------------+--+----------------------+--+------------+
|                      |  |                      |  |            |
+----------------------+--+----------------------+--+------------+
|                      |  |                      |  |            |
+----------------------+--+----------------------+--+------------+
|                      |  |                      |  | 2行とばし |
+----------------------+--+----------------------+--+------------+
|                      |  |                      |  |            |
+----------------------+--+----------------------+--+------------+
|                      |  |                      |  |            |
+----------------------+--+----------------------+--+------------+
|                      |  |                      |  |            |
+----------------------+--+----------------------+--+------------+
| 3行とばし           |  |                      |  |            |
+----------------------+--+----------------------+--+------------+

指定したファイル(sample01.xlsx)に含まれている2つのシート毎に結果が表示されています。このように特にオプションを指定しない場合は全てのシートを対象に表示します。

特定のシートだけを対象にしたい場合は、"--sheets=(シート名)" というオプションで指定します。チート名は半角カンマ(,)で分けて複数指定することも可能です(Sheet2の "2" は全角なので注意してください):
$ xls-viewer --sheets=Sheet2 sample01.xlsx
Sheet2 :
+----------------------+--+----------------------+--+------------+
| 少し長めのテキストを |  |                      |  | 太字       |
| 入力                 |  |                      |  |            |
+----------------------+--+----------------------+--+------------+
|                      |  | 更に123を追加したテ  |  | Italic     |
|                      |  | キスト               |  |            |
+----------------------+--+----------------------+--+------------+
|                      |  |                      |  | 色付き     |
+----------------------+--+----------------------+--+------------+
| aaa                  |  | 3                    |  | 下線       |
+----------------------+--+----------------------+--+------------+
|                      |  | 6                    |  |            |
+----------------------+--+----------------------+--+------------+
|                      |  |                      |  |            |
+----------------------+--+----------------------+--+------------+
|                      |  |                      |  |            |
+----------------------+--+----------------------+--+------------+
|                      |  |                      |  | 2行とばし |
+----------------------+--+----------------------+--+------------+
|                      |  |                      |  |            |
+----------------------+--+----------------------+--+------------+
|                      |  |                      |  |            |
+----------------------+--+----------------------+--+------------+
|                      |  |                      |  |            |
+----------------------+--+----------------------+--+------------+
| 3行とばし           |  |                      |  |            |
+----------------------+--+----------------------+--+------------+

デフォルト指定だと列名や行番号は表示されません。表示したい場合は "--label=1" オプションを付けて実行します:
$ xls-viewer --sheets=Sheet2 --label=1 sample01.xlsx
Sheet2 :
+====+======================+===+======================+===+============+
|    | B                    | C | D                    | E | F          |
+====+======================+===+======================+===+============+
| 2  | 少し長めのテキストを |   |                      |   | 太字       |
|    | 入力                 |   |                      |   |            |
+====+----------------------+---+----------------------+---+------------+
| 3  |                      |   | 更に123を追加したテ  |   | Italic     |
|    |                      |   | キスト               |   |            |
+====+----------------------+---+----------------------+---+------------+
| 4  |                      |   |                      |   | 色付き     |
+====+----------------------+---+----------------------+---+------------+
| 5  | aaa                  |   | 3                    |   | 下線       |
+====+----------------------+---+----------------------+---+------------+
| 6  |                      |   | 6                    |   |            |
+====+----------------------+---+----------------------+---+------------+
| 7  |                      |   |                      |   |            |
+====+----------------------+---+----------------------+---+------------+
| 8  |                      |   |                      |   |            |
+====+----------------------+---+----------------------+---+------------+
| 9  |                      |   |                      |   | 2行とばし |
+====+----------------------+---+----------------------+---+------------+
| 10 |                      |   |                      |   |            |
+====+----------------------+---+----------------------+---+------------+
| 11 |                      |   |                      |   |            |
+====+----------------------+---+----------------------+---+------------+
| 12 |                      |   |                      |   |            |
+====+----------------------+---+----------------------+---+------------+
| 13 | 3行とばし           |   |                      |   |            |
+====+----------------------+---+----------------------+---+------------+

上のラベルを付けた結果を見るとわかりますが、対象のシート内の有効なエリアだけが表示されています(A 列目や1行目にはデータがないので表示されていません)。常に A1 セルも含めて表示する場合は "--a1=1" オプションを付けて実行します:
$ xls-viewer --sheets=Sheet2 --label=1 --a1=1 sample01.xlsx
Sheet2 :
+====+===+======================+===+======================+===+============+
|    | A | B                    | C | D                    | E | F          |
+====+===+======================+===+======================+===+============+
| 1  |   |                      |   |                      |   |            |
+====+---+----------------------+---+----------------------+---+------------+
| 2  |   | 少し長めのテキストを |   |                      |   | 太字       |
|    |   | 入力                 |   |                      |   |            |
+====+---+----------------------+---+----------------------+---+------------+
| 3  |   |                      |   | 更に123を追加したテ  |   | Italic     |
|    |   |                      |   | キスト               |   |            |
+====+---+----------------------+---+----------------------+---+------------+
| 4  |   |                      |   |                      |   | 色付き     |
+====+---+----------------------+---+----------------------+---+------------+
| 5  |   | aaa                  |   | 3                    |   | 下線       |
+====+---+----------------------+---+----------------------+---+------------+
| 6  |   |                      |   | 6                    |   |            |
+====+---+----------------------+---+----------------------+---+------------+
| 7  |   |                      |   |                      |   |            |
+====+---+----------------------+---+----------------------+---+------------+
| 8  |   |                      |   |                      |   |            |
+====+---+----------------------+---+----------------------+---+------------+
| 9  |   |                      |   |                      |   | 2行とばし |
+====+---+----------------------+---+----------------------+---+------------+
| 10 |   |                      |   |                      |   |            |
+====+---+----------------------+---+----------------------+---+------------+
| 11 |   |                      |   |                      |   |            |
+====+---+----------------------+---+----------------------+---+------------+
| 12 |   |                      |   |                      |   |            |
+====+---+----------------------+---+----------------------+---+------------+
| 13 |   | 3行とばし           |   |                      |   |            |
+====+---+----------------------+---+----------------------+---+------------+

各列の幅はデフォルトだと半角20文字となっています。この値を変えて表示するには "--row_max_width" オプションを指定して実行します。以下の例では「半角10文字」で表示しています。B2 セルや D3 セルの長めのテキストが半角10文字で折り返している様子が確認できます:
$ xls-viewer --sheets=Sheet2 --label=1 --a1=1 --row_max_width=10 sample01.xlsx
Sheet2 :
+====+===+============+===+============+===+============+
|    | A | B          | C | D          | E | F          |
+====+===+============+===+============+===+============+
| 1  |   |            |   |            |   |            |
+====+---+------------+---+------------+---+------------+
| 2  |   | 少し長めの |   |            |   | 太字       |
|    |   | テキストを |   |            |   |            |
|    |   | 入力       |   |            |   |            |
+====+---+------------+---+------------+---+------------+
| 3  |   |            |   | 更に123を  |   | Italic     |
|    |   |            |   | 追加したテ |   |            |
|    |   |            |   | キスト     |   |            |
+====+---+------------+---+------------+---+------------+
| 4  |   |            |   |            |   | 色付き     |
+====+---+------------+---+------------+---+------------+
| 5  |   | aaa        |   | 3          |   | 下線       |
+====+---+------------+---+------------+---+------------+
| 6  |   |            |   | 6          |   |            |
+====+---+------------+---+------------+---+------------+
| 7  |   |            |   |            |   |            |
+====+---+------------+---+------------+---+------------+
| 8  |   |            |   |            |   |            |
+====+---+------------+---+------------+---+------------+
| 9  |   |            |   |            |   | 2行とばし |
+====+---+------------+---+------------+---+------------+
| 10 |   |            |   |            |   |            |
+====+---+------------+---+------------+---+------------+
| 11 |   |            |   |            |   |            |
+====+---+------------+---+------------+---+------------+
| 12 |   |            |   |            |   |            |
+====+---+------------+---+------------+---+------------+
| 13 |   | 3行とばし |   |            |   |            |
+====+---+------------+---+------------+---+------------+

デフォルトでは各セルの「表示されている内容」をそのまま表示していますが、セルの表示式が定義されている場合に、その式だけを表示するには "--formula=1" オプションを指定します。実行結果にはセルに定義されている式だけが表示されます:
$ xls-viewer --label=1 --a1=1 --formula=1 sample01.xlsx
Sheet1 :
+===+===+=====+===+===+===+===+
|   | A | B   | C | D | E | F |
+===+===+=====+===+===+===+===+
| 1 |   | 1+2 |   |   |   |   |
+===+---+-----+---+---+---+---+
| 2 |   |     |   |   |   |   |
+===+---+-----+---+---+---+---+
| 3 |   |     |   |   |   |   |
+===+---+-----+---+---+---+---+
| 4 |   |     |   |   |   |   |
+===+---+-----+---+---+---+---+
| 5 |   |     |   |   |   |   |
+===+---+-----+---+---+---+---+

Sheet2 :
+====+===+===+===+======+===+===+
|    | A | B | C | D    | E | F |
+====+===+===+===+======+===+===+
| 1  |   |   |   |      |   |   |
+====+---+---+---+------+---+---+
| 2  |   |   |   |      |   |   |
+====+---+---+---+------+---+---+
| 3  |   |   |   |      |   |   |
+====+---+---+---+------+---+---+
| 4  |   |   |   |      |   |   |
+====+---+---+---+------+---+---+
| 5  |   |   |   | 1+2  |   |   |
+====+---+---+---+------+---+---+
| 6  |   |   |   | D5*2 |   |   |
+====+---+---+---+------+---+---+
| 7  |   |   |   |      |   |   |
+====+---+---+---+------+---+---+
| 8  |   |   |   |      |   |   |
+====+---+---+---+------+---+---+
| 9  |   |   |   |      |   |   |
+====+---+---+---+------+---+---+
| 10 |   |   |   |      |   |   |
+====+---+---+---+------+---+---+
| 11 |   |   |   |      |   |   |
+====+---+---+---+------+---+---+
| 12 |   |   |   |      |   |   |
+====+---+---+---+------+---+---+
| 13 |   |   |   |      |   |   |
+====+---+---+---+------+---+---+

なお "--border=0" オプションを付けると、シートの枠が表示されなくなります:
$ xls-viewer --label=1 --a1=1 --border=0 sample01.xlsx
Sheet1 :
    A   B     C     D  E  F
 1  1   3
 2  3   2     safa        saf
 3  2   sa
 4  43  safd
 5                  5

Sheet2 :
     A  B                     C  D                     E  F
 1
 2      少し長めのテキストを                              太字
        入力
 3                               更に123を追加したテ      Italic
                                 キスト
 4                                                        色付き
 5      aaa                      3                        下線
 6                               6
 7
 8
 9                                                        2行とばし
 10
 11
 12
 13     3行とばし

これらのオプションは組み合わせて使うことも可能です。結果をファイルにリダイレクトしながら使えば後からファイルとして開くこともできて、個人的には我ながら重宝しているコマンドです。



上述の説明では実行可能ファイルだけを紹介していますが、実際にはソースコード含めて公開しています。作り方に興味ある方はこちらもどうぞ:
https://github.com/dotnsf/xls-viewer

2024072300


自分はプログラミングする時、特に制約事項がない場合は Node.js を使います。サーバーサイドアプリだけでなく、CLI 系のコマンドラインアプリも作れるし、React を併用してフロンドエンドアプリも作れます。HTML 内で JavaScript を使うケースも含めると JavaScript 系の言語は使い道が幅広くて便利、と思ってます。

そんな自分が最近作った CLI アプリ(コマンドラインアプリ)がそこそこ便利だったので公開したかったのですが、Node.js アプリは Node.js がインストールされた環境で動くのです。ある意味で当たり前のことを言っているのですが、プログラミングなどに詳しくない人からすると「プログラミング言語をインストールしないと動かない」というのはハードルが高いものです。なんとかして「ダウンロード→即実行可能」な形にできないかと調べてみました。今回のブログエントリはそんな経緯をまとめたものです。

ランタイムが Node.js の場合、このようなアプリケーションのパッケージ化ツールには nexe はじめ、いくつかの選択肢があるようですが、今回自分が使ったのは pkg というツールです※
2014072100


※ Git リポジトリは現在アーカイブされているので、今後のアップデートはされない可能性が高いと思っています。逆に言うと、良くも悪くも今後の変更はほぼないものだと思って使ってください。

自分が Linux(WSL2) で作った CLI アプリが Linux および Windows 向けにクロスコンパイルして実行可能バイナリ化できる所まで確認しました。自分の用途としてはここまでできれば実用的と言えるので、今後はこれを使っていこうかな、と考えています。


【pkg のインストール】
既に Node.js が導入済みの環境であれば、pkg のインストールは以下のコマンドを実行するだけなので超簡単です:
$ sudo npm install -g pkg

npm で pkg をグローバルインストールします。これで pkg コマンドが使えるようになります。


【pkg の使い方】
例えば 64bit Linux(WSL2) で app.js というファイル名の CLI アプリを作ったとします。この CLI アプリは Node.js を使う場合は以下のように実行できるものとします:
$ node app.js

ここに pkg をインストールして、3つの環境(64bit Linux, 64bit MacOS, 64bit Windows)向けの実行可能バイナリを作る場合は以下のコマンドを(特にオプションを付けずに)実行します:
$ pkg app.js

この場合は同じフォルダに app-linux, app-macos, app-win.exe という3つのファイルが生成され、それぞれ 64bit Linux, 64bit MacOS, 64bit Windows 向け実行可能バイナリになっています。

現在の環境と同じ環境(64bit Linux)向けの実行可能バイナリ(app)を作るのであれば、以下のコマンドを実行します("-o" オプションで出力する実行可能バイナリファイル名を指定します):
$ pkg -o app app.js

出力するファイルをクロスコンパイルしたい場合、つまり現在の環境とは異なる環境(例えば 64bit Windows)向けの実行可能バイナリ(app.exe)を作る場合は "-t" オプションでクロスコンパイル先の対象プラットフォームを指定します("node16" は "Node.js v16 でコンパイルする" の意味です):
$ pkg -t node16-win-x64 -o app.exe app.js

対象プラットフォームは node16 までは使えることを確認したのですが(ドキュメントも node16 までは書かれているのですが)、これ以上のバージョンがサポートされているかどうかは未確認です。

この pkg を使って作った実行可能バイナリは小規模なアプリでも 30MB 以上になります。私が作ったアプリも package.json には2つのライブラリしか登録していないのですが、それでも 45MB ほどになりました。おそらく node 本体とライブラリを全部まとめてパッケージングしてるんだろうな・・・と想像しています。


 

このページのトップヘ