🐡

JPYC SDKで何ができる? ECサイトの決済機能をNext.jsで体験してみた(残高表示編)

に公開

こんにちは!Web3特化の開発会社 Komlock labでエンジニアをしている小原です。

普段、私たちがWEB2(既存のWebサービス)でプロダクト開発をしていると、必ずお金の扱いに直面しますよね。

  • 決済手数料(クレカ等)をもう少し安くできないか?
  • 海外ユーザーにも、もっと簡単に日本円で決済してほしい
  • クリエイターへの報酬支払いを、銀行振込より安く、早くできないか?

こうした課題を解決するかもしれない技術として、最近JPYCが注目されています。

JPYCとは? 簡単に言うと、インターネット上で誰でも送受信できる、日本円と同じ価値を持つデジタルなお金です。

「それって電子マネー(PayPayなど)と何が違うの?」と思うかもしれません。 最大の違いは、特定の企業(プラットフォーマー)に依存せず、プログラムで自由に送金や受け取りを自動化できる点です。

このプログラム可能なお金を自社プロダクトに組み込むためのSDKが、最近JPYCから公式に公開されました。

今回は、WEB2エンジニアが最も慣れている Next.js 15 + React 19 の最新構成を使い、自社サイトにJPYCの残高を表示する機能を通じて、その可能性の第一歩を体験してみます。

使用した主なライブラリ

今回の検証は、主に以下のライブラリを組み合わせて使っています。

ライブラリ 役割
RainbowKit MetaMaskなどのウォレットを接続するためのUIコンポーネント。ボタン1つで接続機能を実装できる。
wagmi Ethereum対応DAppでウォレット接続・残高取得・送金などを行うReact Hooks集。
JPYC SDK JPYCトークンの送金・残高取得などを簡単に扱えるSDK。今回はReact Hooks版を利用。
@tanstack/react-query データフェッチやキャッシュ管理を行うライブラリ。非同期状態(読み込み中・成功・エラー)を統一的に扱える。JPYC SDKの内部でも利用されている。

JPYC SDKについて

本記事で使用している @jpyc/sdk-react は、

本記事執筆時点(2025年10月)ではまだnpmに正式リリースされていません。

現状は
https://github.com/jcam1/sdks

develop ブランチに含まれている開発中パッケージをsubmoduleで参照しています。

今回作るプロダクトイメージ:ECサイトの「JPYC決済」機能

SDKを触る前に、最終的なゴールをイメージしてみましょう。 例えば、私たちが運営するECサイトの決済方法選択画面に、JPYCで支払うという選択肢を追加するケースを考えます。

  1. ユーザーがJPYCで支払うを選択。
  2. ウォレットを接続ボタンが表示されます。
  3. ユーザーがウォレットを接続すると、サイトがユーザーのJPYC残高を自動で取得します。
  4. サイト上にあなたのJPYC残高: 5,000 JPYCと表示します。
  5. 将来的には残高が商品代金より多ければ購入確定ボタンが押せるようになります。

今回の記事では、このうちステップ 2〜4 のウォレットを接続し、残高を表示する部分を実装します。ここができれば、WEB2のサービスとブロックチェーン上の資産(JPYC)を繋ぐ最初の関門を突破できます。

環境構築

セットアップ方法はGitHubにまとめています👇

https://github.com/br-to/jpyc-viewer?tab=readme-ov-file#jpyc-viewer

ここでは、重要なところだけをピックアップして紹介します。

JPYCをテストネットで入手する

JPYCはSepoliaテストネット上でも利用できます。

開発用トークンは
https://faucet.jpyc.jp/
から入手可能です。15分間隔で1JPYC〜100,000JPYCを手に入れることができます。

ウォレットは MetaMask を利用しています。
まだ導入していない場合は、公式サイトからブラウザ拡張をインストールしておきましょう。

ただし、ガス代として少量のSepolia ETHが必要です。
持っていない場合は以下のようなFaucetから受け取ってください👇

※ Sepoliaテストネット用 Faucet では、利用するウォレットに Ethereum メインネット上で少額のETHが入っていることを条件としているものがあります。もし 完全に残高ゼロの新規ウォレットを使っていてトークンが取得できない場合は、メインネット上で0.001ETH以上を送ってから試してください。

手順:

  1. MetaMaskを Sepolia ネットワークに切り替える
  2. https://faucet.jpyc.jp/ にアクセス
  3. ウォレットを接続」ボタンから、JPYCを受け取りたいウォレットを接続
  4. 受け取りたいJPYCの量を入力して、「受け取る」ボタンを押下
  5. 数十秒ほどでウォレットにJPYCが反映されます。

最後にトランザクションの完了後に、etherscanで受け取れているか確認しましょう
Etherscanで確認

Amountのところを確認すると100,000分のJPYCが受け取れてますね!

【WEB2エンジニア向け】初めてのWEB3用語解説

ここでいくつか専門用語が出てきましたので、簡単に解説します。

  • ウォレット (Wallet)

WEB3のお財布アプリです。JPYCのようなデジタル資産を保管します。
WEB2でいうID/パスワードや銀行口座のように、サービスに認証するためにも使います。

  • テストネット

開発用のお試し環境です。本番(メインネット)とは隔離されています。
WEB2でいうStaging環境や開発環境と同じです。ここでは価値のないテスト用のJPYCを使って、安全に機能をテストできます。

  • ガス代

ブロックチェーンのネットワーク利用料です。
送金などの操作(トランザクション)を行う時に、手数料としてETH(イーサ)という通貨で支払います。テストネットなので、これもテスト用のETHを使います。

  • Faucet (フォーセット)

このテスト用のJPYCやテスト用のETHを無料でもらえるサイトのことです。

残高を取得して表示する

では、ウォレットを接続してJPYCの残高を取得してみます。
そのために、まずは wagmiRainbowKit、そして JpycSdkProvider を設定します。

Provider設定

app/provider.tsx

'use client';

import { WagmiProvider } from 'wagmi';
import { JpycSdkProvider } from '@jpyc/sdk-react';
import { sepolia } from 'wagmi/chains';
import { QueryClientProvider, QueryClient } from '@tanstack/react-query';
import { RainbowKitProvider, getDefaultConfig } from '@rainbow-me/rainbowkit';
import '@rainbow-me/rainbowkit/styles.css';

const queryClient = new QueryClient();

const config = getDefaultConfig({
  appName: 'JPYC Sample Viewer',
  projectId: process.env.NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID || '',
  chains: [sepolia],
  ssr: true,
});

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <WagmiProvider config={config}>
      <QueryClientProvider client={queryClient}>
        <RainbowKitProvider>
          {/* env="prod"の時SDKが自動的にJPYC_PREPAID_PROXY_ADDRESS (0x431D5dfF...)を選択 */}
          <JpycSdkProvider
            env="prod"
            contractType="jpycPrepaid"
            localContractAddress={undefined}
          >
            {children}
          </JpycSdkProvider>
        </RainbowKitProvider>
      </QueryClientProvider>
    </WagmiProvider>
  );
}

環境変数の設定

この構成では、WalletConnectJPYCのsepoiaコントラクトアドレス の2つを環境変数で指定します。

Next.jsでは .env.local に以下を追記します👇

NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=your_project_id_here

作成したprovidersを、アプリ全体に適用するためにapp/layout.tsxに組み込みます。

これで、どのページからでもwagmiJPYC SDKのHooksを直接使えるようになります。

残高表示の実装

ここでは、ウォレット接続済みのユーザーのJPYC残高を画面に表示する部分を実装します。

app/page.tsx でウォレット接続と残高表示を行います。

'use client';

import { ConnectButton } from '@rainbow-me/rainbowkit';
import { useAccount } from 'wagmi';
import { JPYCInfo } from '../components/JPYCInfo';

export default function Home() {
  const { address, isConnected } = useAccount();

  return (
    <div className="font-sans grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20">
      <main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
        <div className="flex flex-col items-center gap-8">
          <h1 className="text-4xl font-bold text-center">JPYC Viewer</h1>
          <div className="text-center text-sm text-gray-600 dark:text-gray-400 max-w-md">
            <p>Sepoliaテストネットワーク上のJPYCトークン情報を表示します</p>
            <p className="mt-2">💡 テスト用JPYCの残高と情報を確認できます</p>
          </div>
          <ConnectButton />

          {isConnected && (
            <div className="space-y-6">
              <div className="bg-gray-100 dark:bg-gray-800 p-6 rounded-lg max-w-md">
                <h2 className="text-xl font-semibold mb-4">ウォレット情報</h2>
                <div className="space-y-2">
                  <p className="text-sm break-all">
                    <strong>アドレス:</strong> {address}
                  </p>
                </div>
              </div>
              <JPYCInfo />
            </div>
          )}
        </div>

        {!isConnected && (
          <div className="text-center text-gray-600 dark:text-gray-400">
            <p>ウォレットを接続してJPYC情報を表示してください</p>
          </div>
        )}
      </main>
    </div>
  );
}

src/components/JPYCInfo.tsx を作成します。

import { useBalanceOf, useTotalSupply } from '@jpyc/sdk-react';
import { useAccount } from 'wagmi';

export function JPYCInfo() {
  const { isConnected, chain, address } = useAccount();

  // JPYC SDK hooks
  const {
    data: balanceData,
    isPending: isBalanceLoading,
    error: balanceError,
  } = useBalanceOf({
    account: address as `0x${string}`,
  });
  const totalSupplyResult = useTotalSupply({});
  const totalSupplyData = totalSupplyResult?.data;
  const isTotalSupplyLoading = totalSupplyResult?.isPending || false;

  if (!isConnected || !address) {
    return null;
  }

  const formattedBalance = parseFloat(balanceData || '0');
  const formattedTotalSupply = parseFloat(totalSupplyData || '0');

  return (
    <div className="bg-blue-50 dark:bg-blue-900/20 p-6 rounded-lg border border-blue-200 dark:border-blue-800 max-w-md">
      <h2 className="text-xl font-semibold mb-4 text-blue-800 dark:text-blue-200">
        JPYC トークン情報
      </h2>
      <div className="space-y-3">
        <p className="text-sm">
          <strong>トークン名:</strong> JPY Coin
        </p>
        <p className="text-sm">
          <strong>シンボル:</strong> JPYC
        </p>
        <p className="text-sm">
          <strong>残高:</strong>{' '}
          {isBalanceLoading ? (
            <span className="text-gray-500">読み込み中...</span>
          ) : balanceError ? (
            <span className="text-red-500" title={balanceError.message}>
              エラー: {balanceError.message}
            </span>
          ) : (
            `${formattedBalance.toLocaleString('ja-JP', {
              minimumFractionDigits: 0,
              maximumFractionDigits: 6,
            })} JPYC`
          )}
        </p>
        <p className="text-sm">
          <strong>総供給量:</strong>{' '}
          {isTotalSupplyLoading ? (
            <span className="text-gray-500">読み込み中...</span>
          ) : (
            `${formattedTotalSupply.toLocaleString('ja-JP', {
              minimumFractionDigits: 0,
              maximumFractionDigits: 6,
            })} JPYC`
          )}
        </p>
        <p className="text-sm">
          <strong>ネットワーク:</strong> {chain?.name || 'Unknown'}
        </p>
      </div>
    </div>
  );
}

JPYC SDKのHooks解説

このコンポーネントで使用したhooksは、

@jpyc/sdk-react に含まれる JPYC専用のReact hooks です。

今回使用したhooks

Hook名 説明 戻り値
useBalanceOf 指定したアカウントのJPYC残高を取得 { data, isPending, error }
useTotalSupply JPYCトークンの総供給量を取得 { data, isPending, error }

どちらも isPendingerror の状態を持っているので、

ロード中やエラー表示を簡単に扱えます。

 const {
    data: balanceData,
    isPending: isBalanceLoading,
    error: balanceError,
  } = useBalanceOf({
    account: address as `0x${string}`,
  });

if (isBalanceLoading) return <p>読み込み中...</p>;
if (balanceError) return <p>エラー: {balance.error.message}</p>;

return <p>残高: {balanceData} JPYC</p>;

他にも使えるHooks

JPYC SDKには他にも、送金や承認などを行うHooksが用意されています。

Hook名 用途
useTransfer JPYCを指定アドレスに送金
useApprove 指定アドレスにトークンの送金権限を与える(DeFi連携用)
useAllowance 承認済みアドレスの送金上限を取得

たとえば useTransfer を使えば、ウォレット接続済みユーザーがJPYCを別アドレスに送る送金UIを簡単に作れます。

const { transfer, isLoading, isSuccess, error } = useTransfer();

const handleSend = async () => {
  try {
    await transfer({
      to: '0x1234...abcd',
      value: 10, // 10 JPYC
    });
  } catch (err) {
    console.error('送金エラー:', err);
  }
};

これらも全てReact Queryベースで非同期状態を管理しており、エラー処理やロード表示を統一的に扱えます。

表示結果

ウォレット接続後、以下のように表示されます。

デモ環境(Vercel)

実際に動作を確認できる検証環境を公開しています👇

https://jpyc-viewer.vercel.app/

まとめ

今回、JPYC SDK(React版)を使い、Next.jsアプリからブロックチェーン上のJPYC残高を表示してみました。

useBalanceOfといったReact Hooksを使うだけで、WEB2エンジニアがAPIを叩くのと同じ感覚で、ブロックチェーン上の資産を扱えたのは非常に良い開発体験でした。

これまで、プロダクトに決済送金を組み込むのは大変でした。決済代行会社(StripeやPayPayなど)との契約・API連携が必須で、手数料もかかります。

JPYC SDKを使えば、特定の企業に依存しないお金の送受信機能を、Reactコンポーネントを扱うのと同じくらい手軽に実装できる可能性が見えてきました。

今回は残高表示まででしたが、SDKには useTransfer(送金)や useApprove(送金承認)といったHooksも用意されています。

これらを使えば、

  • ECサイトの決済(今回イメージしたもの)
  • クリエイターへのリアルタイム報酬支払い
  • ユーザー間でのポイント(JPYC)送金

といった機能を、銀行振込や従来の決済システムよりも安く、早く、グローバルに実装できるかもしれません。

ぜひ、あなたのプロダクトでもプログラム可能なお金の活用を検討してみてはいかがでしょうか。

もしこの記事が役立ったら、X(@brto_0224)で感想などもらえると嬉しいです。

また週一ペースで、主にWeb3関連の記事を投稿しています。
Xのフォローしてもらえると励みになります🙏

Komlock lab

Discussion