まいにちDappsの2日目は、前日に続いてNFTのmintサイトを作成しますが、クレジットカード💳での支払いに対応していきます。私たちが受託開発を行う際、クライアントからは非クリプトユーザーに対応した機能の1つとしてクレカ決済が要望されることがあります(個人的には必ずしも良い策だとは思わないですが😅)。
今回は以下のいくつかの方法を試してみます。
- Paper.xyzのCheckout機能
- Crossmint
- Stripeで実装する方法
Paper.xyzのCheckout機能
Paper.xyzはサービス内ウォレットの導入(Embedding Wallet)や今回使用するCheckout機能などNFT・Dapps開発者のためのツールを提供してくれているサービスです。先日Thirdwebに買収されたことを発表しました。
記事はかなり古いものなので、UIは現在とは異なっていますが、こちらの記事を参考に進めていきます。
https://withpaper.com/dashboard/contracts にアクセスします。Paper.xyzを使ってNFTのコントラクトを新規に作成することも可能ですが、今回はThirdwebで既に作成してあるコントラクトを使いたいので、"Register Contract"ボタンを押します。
![Image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffrqcsxz2%2Fproduction%2F70e7687dce8f1289c8382e368e1c6b82ebaab285-2138x914.png%3Fw%3D2000%26auto%3Dformat&w=3840&q=75)
Import Contract URLを選択し、ThirdwebのURLを入力します。
![Image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffrqcsxz2%2Fproduction%2F53ea23bf9ee4caf454346d6399ed54041cf5eec4-1240x870.png%3Fw%3D1240%26auto%3Dformat&w=3840&q=75)
まいにちDapps 1日目で作成したNFT Dropのコントラクトをそのまま使用します。
![Image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffrqcsxz2%2Fproduction%2F0f01f4471ada3e540ccb8837191f9dc082b60f5a-2070x680.png%3Fw%3D2000%26auto%3Dformat&w=3840&q=75)
クレカ決済ができる販売リンクが生成されます。
まずNFTを受け取るWalletをConnectする必要がありますが、先述したEmbedding Walletの機能を使ったサービス内ウォレットをemailベースで作成することもできます。
![Image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffrqcsxz2%2Fproduction%2F98385a732719f3a65b6661124e501c84494de13d-2666x1400.png%3Fw%3D2000%26auto%3Dformat&w=3840&q=75)
![Image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffrqcsxz2%2Fproduction%2Fc4d4931c5feade73aa526b8ad25e95923401486b-1374x932.png%3Fw%3D1374%26auto%3Dformat&w=3840&q=75)
![Image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffrqcsxz2%2Fproduction%2F15e0fed88805e2d42a2e12e5e8b4c220c7acf410-996x756.png%3Fw%3D996%26auto%3Dformat&w=3840&q=75)
EmailにPINコードが送られてくるのでそれを使用するとウォレットが作成されてクレジットカード画面に遷移できます。
![Image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffrqcsxz2%2Fproduction%2F1abcdad1857439eec0f27c8b559dba86d73ef623-1656x1012.png%3Fw%3D1656%26auto%3Dformat&w=3840&q=75)
支払われた売り上げの情報はDashboardで確認して、本人確認等が済んだのち引き出すことができます。
![Image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffrqcsxz2%2Fproduction%2F092423ac361465c76f309d5993b3330fdd8f3b6b-2100x1454.png%3Fw%3D2000%26auto%3Dformat&w=3840&q=75)
また自分のサイトに独自UIで埋め込むこともできるみたいです。
https://docs.withpaper.com/reference/checkoutwithcard
Paperが購入者側から手数料を徴収するのでその点ご注意下さい。
Crossmintの導入
Paper.xyzと似たようなサービスでCrossmintがあります。
こちらの記事と公式ドキュメントのQuickStartを元に進めていきます。
staging.crossmint.com/console にアクセスしてDeveloperアカウントを作成します。
https://staging.crossmint.com/console/collections に行き、NFTコントラクトを登録します。
![Image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffrqcsxz2%2Fproduction%2F7c4d114d81462340f622cf1f78cf5ed061a860f5-2734x1448.png%3Fw%3D2000%26auto%3Dformat&w=3840&q=75)
![Image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffrqcsxz2%2Fproduction%2F8e24e2a91f44b176268ba67cd644df1fe9566905-2134x1402.png%3Fw%3D2000%26auto%3Dformat&w=3840&q=75)
![Image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffrqcsxz2%2Fproduction%2F03de8f394dd8c7ed9bf2fe4190b6961a255bca9e-1812x1312.png%3Fw%3D1812%26auto%3Dformat&w=3840&q=75)
ダッシュボードに反映されます。
![Image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffrqcsxz2%2Fproduction%2Fddc3ca561beb471336c6ec7b2e73b6b67bd0efd4-2556x1540.png%3Fw%3D2000%26auto%3Dformat&w=3840&q=75)
Next.jsのプロジェクトを立ち上げて導入してみます。
"use client";
import { CrossmintPayButton } from "@crossmint/client-sdk-react-ui";
export default function Home() {
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<CrossmintPayButton
collectionId="a1f6cca0-1c24-4226-92c5-f50a85bbd965"
projectId="28990d6d-1252-4c45-a89f-1a7e0eb4e5bc"
mintConfig={{
type: "thirdweb-drop",
totalPrice: "0.01",
quantity: "1",
}}
environment="staging"
/>
</main>
);
}
mintボタンが生成されます。
![Image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffrqcsxz2%2Fproduction%2Ffbc8906dd8ea54fad672efaf2a49377f055e0fcb-1278x580.png%3Fw%3D1278%26auto%3Dformat&w=3840&q=75)
![Image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffrqcsxz2%2Fproduction%2F747c267755f5f471596ddd8b9adfc39a6db9aed6-894x1300.png%3Fw%3D894%26auto%3Dformat&w=3840&q=75)
![Image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffrqcsxz2%2Fproduction%2F2fcd83d056f2b16b7e5a07b562ba4fdeb53c20b0-2398x1514.png%3Fw%3D2000%26auto%3Dformat&w=3840&q=75)
決済部分にはStripeが使われているようです。
ちなみにStripeのステージングモードではクレカ番号は424242424242で行けます。
![Image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffrqcsxz2%2Fproduction%2F0685f1b6912096bbaaf0873086f46656927d0fe8-898x1238.png%3Fw%3D898%26auto%3Dformat&w=3840&q=75)
![Image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffrqcsxz2%2Fproduction%2F241f513d94ed7639a0175936b3deb4402910aeaf-1054x1346.png%3Fw%3D1054%26auto%3Dformat&w=3840&q=75)
一度crossmint内に生成されたウォレットに対してmintされているようです。
![Image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffrqcsxz2%2Fproduction%2F0f809ef5f5907e54dbf1ae2af84a33d007419e83-1344x1246.png%3Fw%3D1344%26auto%3Dformat&w=3840&q=75)
好きなウォレットにガスレスで送ってくれるようです。
![Image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffrqcsxz2%2Fproduction%2F9bb2ff247e8c7cb5ccc594b44e73c8a1edd00872-1156x498.png%3Fw%3D1156%26auto%3Dformat&w=3840&q=75)
こちらも十分使いやすいと思いました。
Stripeで実装
最後に比較的自力で実装するケースを紹介します。
アーキテクチャとしてはシンプルで、Stripeでクレカ決済を行い、成功したらwebhookをキャッチしてそこでNFTをウォレットに対してmintするというものです。
Thirdwebでコントラクトをデプロイしておきます。
次に、Stripeのテストモードで商品を準備します。Productページから "+Add Product" をクリックして商品を追加します。subscriptionではなく単品にします。
![Image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffrqcsxz2%2Fproduction%2F935b3b8ec464a514533c38d171e5b3fd4221728a-2836x560.png%3Fw%3D2000%26auto%3Dformat&w=3840&q=75)
作成した商品のprice_idと開発者アカウントのSecret_Keyを取得しておきます。
次にNFT mintサイトを作ります。Next.jsのプロジェクトを立ち上げます。
トップページにボタンを設置、API層にstripe-checkoutというapiを生やします。
import { NextResponse } from "next/server";
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY || "", {
apiVersion: "2022-11-15",
});
export async function POST(request: Request) {
const data = await request.json();
const { address } = data;
const session = await stripe.checkout.sessions.create({
line_items: [
{
price: process.env.STRIPE_PRICE_ID,
quantity: 1,
},
],
payment_method_types: ["card"],
mode: "payment",
success_url: `${process.env.NEXT_PUBLIC_BASE_URL}/success`,
cancel_url: `${process.env.NEXT_PUBLIC_BASE_URL}/?canceled=true`,
allow_promotion_codes: true,
metadata: { address: address },
});
return NextResponse.json({
session_id: session.id,
checkout_url: session.url,
});
}
"use client";
import { ConnectWallet, useAddress } from "@thirdweb-dev/react";
import { useRouter } from "next/navigation";
export default function Home() {
const address = useAddress();
const router = useRouter();
const payment = async () => {
const response: any = await fetch(`/api/stripe-checkout`, {
method: "POST",
body: JSON.stringify({
address: address,
}),
});
console.log(response);
const result = await response.json();
router.push(result.checkout_url);
};
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
{address ? (
<button
type="button"
onClick={payment}
className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800"
>
Payment
</button>
) : (
<ConnectWallet></ConnectWallet>
)}
</main>
);
}
ボタンを押すとStripeの決済画面に飛ぶ仕組みができました。
![Image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffrqcsxz2%2Fproduction%2F165078e36c300df998684c46c1c3f89e825c56c8-1956x1420.png%3Fw%3D1956%26auto%3Dformat&w=3840&q=75)
webhookを作ります。
https://dashboard.stripe.com/test/webhooks
テストするにはwebhookのエンドポイントをリモートからアクセス可能にする必要があるのでvercelにデプロイするか、開発中であればngrokなどで一時的なurlを取得する方法があります。
![Image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffrqcsxz2%2Fproduction%2F753ec73453d1bce9fa03403b6096404461695922-2084x1506.png%3Fw%3D2000%26auto%3Dformat&w=3840&q=75)
上記画像の右側にあるコードをNextに適切なコードに変更します。
import { headers } from "next/headers";
import { NextResponse } from "next/server";
import { ThirdwebSDK } from "@thirdweb-dev/sdk";
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY || "", {
apiVersion: "2022-11-15",
});
export async function POST(request: Request) {
const bodyString = await request.text();
const body = JSON.parse(bodyString);
const signature = headers().get("Stripe-Signature") as string;
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(
bodyString,
signature,
process.env.STRIPE_ENDPOINT_SECRET || ""
);
} catch (err: any) {
return NextResponse.error();
}
switch (event.type) {
case "checkout.session.completed":
const checkoutSessionCompleted = event.data.object;
// Then define and call a function to handle the event checkout.session.completed
const sdk = ThirdwebSDK.fromPrivateKey(
process.env.PRIVATE_KEY || "",
"mumbai",
{
secretKey: process.env.SECRET_KEY, // Use secret key if using on the server, get it from dashboard settings
}
);
const contract = await sdk.getContract(
"0xE71eE93Ad57c8b355e1bCDFe435B05673d8B4930"
);
const tx = await contract.erc721.claimTo(
body.data.object.metadata.address,
1
);
console.log(tx);
break;
// ... handle other event types
default:
console.log(`Unhandled event type ${event.type}`);
}
return NextResponse.json({});
}
デプロイして試してみました。Polygonscanでも確認できているのでOKそうです。
![Image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffrqcsxz2%2Fproduction%2Fede058dc401187a11f5d17845aa6a8c5a611d2c2-1514x596.png%3Fw%3D1514%26auto%3Dformat&w=3840&q=75)
まとめ
以上今回はクレジットカード決済をNFT mintサイトに導入する3つの実装方法をご紹介しました。いずれの方法も本番環境で行うには発行者の事業体の審査が必要になるのでご留意ください。
日本の事業者さんでPiementというサービスが提供されているようです!こちらは試せていないのですが、LPを見る限り、NFTプロジェクト側の手数料は無料で導入でき、ユーザーに支払ってもらうビジネスモデルで運営されているようです。
弊社Pontechはweb3に関わる開発を得意とするテック企業です。サービス開発に関するご相談はこちらのフォームからお願いいたします。
また、受託開発案件に共に取り組むメンバーを募集しています!ご興味のある方はぜひお話させてください!