2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

コンテンツマーケット作ったら決済プラットフォーム全部使えんようになって地獄を見た話

Last updated at Posted at 2025-11-24

決済を軽く見るとサービスが死ぬ

これは決済を実装した体験談や決済関連(クレカ、QRコード、何でもOK!)の情報を投稿しよう! by PAY Advent Calendar 2025 用の記事である。

アプリでもオンラインショップでも作ったプロダクトから収入を得ようと思えば、オンライン決済をどうするかは超重要で、どれを選ぶかはサービスの成否を左右すると言えよう。
これは、数年前に数ヶ月を費やして開発したアプリがあり、最後の決済の実装で地獄を見た話である。

序 - DIYクリエーター用の自由なマーケットを作ったつもりやった

今から多分3年くらい前なんだけど、TikTokとか、Insta、Youtubeとか、どうでもいいような短い動画がタイムラインを濁流のように流れていくのを眺めながら貴重な時間を湯水の如く浪費していたわけですが、世界中にこれだけ動画を作ってアップしてる人がいるわけで、中にはキラッと光るようなものを作ってる人もいるし、特にお色気系や趣味系は侮れない。

そういう短い動画を作っているセンスのある素人が、これでお小遣い稼ぎを出来たら良いよねと思い立って、ちょうどReactを勉強していたこともあり、そういうコンテンツマーケットみたいなものを作ってみようと思い立ったんですよ。

特に東南アジア諸国は人口ピラミッドも綺麗な形で、若くてエネルギーに溢れた若者たちが大量に職にあぶれて貴重な時間を浪費している。
動画、画像、オリジナルのレシピや自作の楽曲なんかを売れるデジタルマーケットプレイス。 ええやんこれ。

jpegOutput-1-1-1280x720.jpg

でも、ある意味それなら自分のTikTokやらInstaやらのSNSでやれば良いわけで、やるなら少し違ったコンセプトにしないと意味がないと思い、オープンなSNSと逆のクローズドマーケットっていうコンセプトで、ある程度自分の事を知っている人やファン的な人にしか売れないような場所、売り手からキーをもらわないとアクセスできないような感じにしようと思って、開発を始めたわけです。

その当時はまだAIエージェントなんてない頃だったので、各要素技術、例えば動画や画像をサーバー側で処理したり、各トランザクション毎にQRコードを生成してコンテンツに期間限定でアクセス出来るようにしたりと、かなり時間をかけて、3か月くらいでようやくアプリ自体は動くようになったのでした。

  • ゲストはログイン不要で買い物可能
  • コンテンツのID(ルームキー)を知らないとアクセス出来ない
  • コンテンツへのアクセス期間設定機能
  • ブロック機能
  • チャット
  • クリエーターのMLM的な紹介制度とコミッションの導入

等など実装して、
UI も MUI + React でモダンなものにし、バックエンドもFirebaseで抜かりは無し。
後はStripeでグローバル決済や!
これは行けるで!Boooommmmm!!!!!

…問題はここからやった。

決済、最初の壁

tak__--chaos_5_--ar_43_--raw_--stylize_150_--v_7_9a0b1af1-05f6-4278-abd7-2ae91fec23f0_3.png

とりあえずStripeとかPaypalとかメジャーどころのサービスを使ったらいいやろと決済を軽く考えてたのですが、今自分の住んでいるフィリピンでは使われへんやん。
この辺は、信用力の高い日本に住んでいると、なかなかイメージ出来ないのだが、日本に住んでいてこう言った類のサービスを使おうと思えば、大抵は使える。
ただ、フィリピンというのはまだまだ修羅の国、ドラッグ、汚職、詐欺といったイメージがあり、信用力が低く、こういった支払い系のサービスの大半は使えないのである。

その他、色々なメジャーなサービスを使おうとしたんだけど、大量の書類を出して、認証されないとAPIキーをもらえない。
そもそも契約は法人でないといけない。
この時点で、個人開発デベロッパーは無理である。

大手決済ぜんぶ死亡:どうしたらええんや

shocker.jpg

で、なんとかフィリピンローカルのPayMongoという、Stripeみたいなサービスが個人事業主レベルでも使えそうだという事を発見し、なんとかこれでリリースできそうやと、まずサンドボックス用のAPIで決済を埋め込んでみる。

今の決済サービスはサービス提供プラットフォームのAPIにリクエストを送れば、後はプラットフォーム側の決済ページにリダイレクトされ、ユーザーはそこで決済して終わりなので、本当に簡単である。
決済が完了したかどうかはWebhookで分かる。
開発側は、注文情報をAPIに送り、フロント側にレスポンスを返送するコードと、Webhook受け取り時のコードをサーバーサイドに書けば良いだけである。
因みに、決済の基本的なフローはこんな感じである。

  1. ユーザーが支払いボタンをクリック
  2. バックエンドのAPIエンドポイントに商品情報とか金額のデータを送信
  3. バックエンドからペイメントプラットフォームのAPIに情報送信、支払いに必要な情報が入ったセッションオブジェクト(1 transaction, 1 object)が作られる
  4. 支払いページへのリダイレクトURLがフロントに返ってくる
  5. カード情報等を入力。支払い手続き。
  6. 支払い完了ページにリダイレクト
  7. バックエンドのWebhookに、支払いのステータスや情報が返ってくるので、データベースに保存

ここはQIITAなので、一応参考のコードでも書いておく。

サンプルコード(フロント)

// src/CheckoutButton.jsx
import React, { useState } from "react";

const API_BASE = "http://localhost:4242";

export default function CheckoutButton() {
  const [loading, setLoading] = useState(false);

  const handleCheckout = async () => {
    try {
      setLoading(true);

      const res = await fetch(`${API_BASE}/create-checkout-session`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(
           {
             quantity: 1,
             amount: 10,
             item: "Secret item 1"
           }
        ), // payload
        });

      const data = await res.json();

      if (data.url) {
        // 決済ページにリダイレクト
        window.location.href = data.url;
      } else {
        alert("Failed to create checkout session");
      }
    } catch (err) {
      console.error(err);
      alert("Error creating checkout session");
    } finally {
      setLoading(false);
    }
  };

  return (
    <button onClick={handleCheckout} disabled={loading}>
      {loading ? "Redirecting..." : "Pay $10"}
    </button>
  );
}

サンプルコード(バックエンド)

// server.js
require("dotenv").config();
const express = require("express");
const Stripe = require("stripe"); //Stripe使った場合
const cors = require("cors");

const app = express();
const stripe = Stripe(process.env.STRIPE_SECRET_KEY);

app.use(cors());
app.use(express.json());

// -------------------------------
// Create Checkout Session (MAIN PART)
// -------------------------------
app.post("/create-checkout-session", async (req, res) => {
  try {
    const { quantity, amount, item } = req.body;

    // Create Stripe Checkout Session
    const session = await stripe.checkout.sessions.create({
      mode: "payment",
      payment_method_types: ["card"],

      line_items: [
        {
          price_data: {
            currency: "usd",
            product_data: { name: item },
            unit_amount: amount * 100,
          },
          quantity,
        },
      ],

      success_url: `${process.env.FRONTEND_URL}/success?session_id={CHECKOUT_SESSION_ID}`, // 支払い完了時に、ユーザーをリダイレクトするページURL
      cancel_url: `${process.env.FRONTEND_URL}/cancel`,
    });

    // フロント側にリダイレクトのURLを返す
    res.json({ url: session.url });

  } catch (err) {
    console.error("Error creating session:", err);
    res.status(500).json({ error: "Failed to create checkout session" });
  }
});

// -------------------------------
// Stripe Webhook (payment result)
// -------------------------------
app.post(
  "/webhook",
  express.raw({ type: "application/json" }),
  (request, response) => {
    let event;
    try {
      event = JSON.parse(request.body); 
    } catch (err) {
      console.error("Webhook error:", err.message);
      return response.sendStatus(400);
    }

    // Handle successful payment
    if (event.type === "checkout.session.completed") {
      const session = event.data.object;
      console.log("💰 Payment succeeded for session:", session.id);

      // 取引や顧客情報をデータベースに書き込んだり、支払い後の色々な処理のコード
      
    }

    response.sendStatus(200);
  }
);

app.listen(4242, () => {
  console.log("🚀 Server running on http://localhost:4242");
});


ある程度バックエンドのプログラミングを知っていれば、かなり簡単に組み込めるのではないであろうか。

で、PayMongoの決済機能が無事に実装され、全てが完璧に動いている。
後はこれをプロダクションモードにするだけである。もう一歩や!

PayMongoも撃沈

tak__--chaos_5_--ar_43_--raw_--stylize_150_--v_7_bbd8d079-e21d-4a12-8c05-f175b9c116d9_3.png

が、APIのプロダクションキーをリクエストすると、さらに追加の書類や情報をリクエストされる。
とりあえず会社でないといけないので、フィリピン人の知り合いに頼んで、バーチャルオフィスのアドレスをかり、個人事業登録をしてもらい、住所や会社情報を送る。
自社サイトのURLも送れという事なんで、サイトもWordPressで急遽制作する。
サービス内容なども送るのだが、なかなか承認されない。
ここで、ひょっとしてこのアプリのサービス自体がやばいんじゃないかと気づく。
そうである。すごく簡単にいうと、FC2のコンテンツマーケットそっくりだったのである。
紳士の方々はお気付きかもしれないが、こういったデータコンテンツを売るというサービスはピンクの方や、薬物売買、情報商材などの深海の方に沈んで行きがちで、そっち方面のユーザーが集まりやすい。そして、オンラインの澱んだ沼地と化していく傾向にある。
そういったマーチャントは問答無用でハイリスクと見なされ、決済プラットフォーム側は非常に敬遠する。
Tシャツや日用品を売るようなサイトとは扱いが全く違うのである。

仕方ないから“クセ強め海外決済”へ亡命を決意

tak_Fight_scene_with_many_scary_monster_at_the_same_time_load_875f7f71-d572-42c3-8bfa-6026a40c04c9_2.png

何ヶ月もかけてアプリを開発し、現地法人まで立ち上げて、このままでは諦められんと思い、「Dating OK / Adult OK / High risk OK」みたいな決済サービスを探す。

結果:

  • 月の取り扱い額1千万円以上、手数料 10〜15%
  • やたらとTelegramでのやり取りを要求される
  • サポートが1か月以上返事ない
  • 担当者のインド人率が異常
  • EUか東欧、アメリカでの会社登記が必要

これは修行アプリかな?

そしてWEB3決済へ

tak___--chaos_5_--ar_43_--raw_--stylize_150_--v_7_9a1b756c-ce77-4340-8226-81e64d494431_3.png

最終的に、こうなったら暗号通貨での支払いを受け付けられるようにしたら良いんやないかと思い、ウォレット決済について色々と調べて実装しようと思ったのだけど、そうなるとそもそも暗号通貨のウォレットを持っている人しか買えなくなってくる。

確かに、UI上はドル表示で、カードでドル決済という感じにしておき、USDtみたいなテザーコインを噛ませて、実はバックエンドではダイレクトにウィレット決済されているみたいな仕組みにも出来るんだけど、それをするとまた カード→暗号通貨 というオンランプと言われている部分をやってくれるサービスプロバイダーを探さないといけない。

色々とオンランププロバイダーに問い合わせたのだが、これは上記クセ強系決済業者の世界であり、うまく行きそうな気がしない、さらにそういった海外の業者連中とTelegramを通じてやり取りしていると、何か自分が非常に違法なサービスを開発してるんじゃないかという気分になってくる。

長かった戦いの終結、そして一つの悟りへ

いろいろ試して最後に残ったのはこういう結論であった。

  • 大手決済 → ほぼ使えん
  • クセ強め決済 → 高い&クセ強い、危険
  • Web3 → 技術的には可能やけどユーザがかなり限られる。やはりカードや電子マネーで払えないと無理

そして、最終的におれは悟った。

自作のコンテンツを売りたい人は、TikTokとかYoutube、Patreonでええやないかと

tak__--chaos_5_--ar_43_--raw_--stylize_150_--v_7_cd204087-8bd3-4a21-8c35-de5a3450ecf1_3.png

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?