[{"data":1,"prerenderedAt":2963},["ShallowReactive",2],{"content-/chat-story-implementation-guide":3,"all-pages-for-dir":2945,"related-/chat-story-implementation-guide":2946,"og-image-/chat-story-implementation-guide":2962},{"id":4,"title":5,"body":6,"category":2933,"concepts":2933,"description":2934,"extension":2935,"meta":2936,"navigation":315,"ogImage":2933,"path":2937,"project_name":2933,"published":2938,"publishedAt":2939,"seo":2940,"source":2933,"stem":2941,"tags":2942,"todo":2933,"unpublished":2938,"updatedAt":2939,"__hash__":2944},"pages/2025-12/2025-12-11/chat-story-implementation-guide.md","Chat Story 実装ガイド",{"type":7,"value":8,"toc":2891},"minimark",[9,13,40,43,47,50,106,108,112,116,119,153,156,218,221,232,236,462,675,678,754,757,780,782,786,790,801,893,897,901,947,950,956,959,965,968,1159,1485,1489,1492,1557,1560,1583,1585,1589,1592,1595,1627,1630,1694,1698,1704,1707,1710,1886,1890,1991,2179,2183,2535,2538,2572,2575,2598,2600,2604,2608,2619,2625,2629,2635,2639,2645,2647,2651,2727,2729,2733,2736,2788,2792,2808,2812,2828,2830,2834,2837,2848,2851,2862,2865,2876,2878,2887],[10,11,5],"h1",{"id":12},"chat-story-実装ガイド",[14,15,16,20,21,27,30,33,34,36,39],"p",{},[17,18,19],"strong",{},"対象サービス",": ",[22,23,24],"a",{"href":24,"rel":25},"https://chat-story.number55number55.workers.dev/",[26],"nofollow",[28,29],"br",{},[17,31,32],{},"作成日",": 2025年12月11日",[28,35],{},[17,37,38],{},"プラットフォーム",": Cloudflare Workers",[41,42],"hr",{},[44,45,46],"h2",{"id":46},"概要",[14,48,49],{},"本ドキュメントは Chat Story サービスに以下の機能を実装するための技術選定と実装方針をまとめたものです。",[51,52,53,69],"table",{},[54,55,56],"thead",{},[57,58,59,63,66],"tr",{},[60,61,62],"th",{},"機能",[60,64,65],{},"推奨技術",[60,67,68],{},"代替選択肢",[70,71,72,84,95],"tbody",{},[57,73,74,78,81],{},[75,76,77],"td",{},"認証（Auth）",[75,79,80],{},"Better Auth",[75,82,83],{},"Clerk, Auth0, Lucia",[57,85,86,89,92],{},[75,87,88],{},"決済（Payment）",[75,90,91],{},"Polar.sh",[75,93,94],{},"Creem.io, Stripe + Tax",[57,96,97,100,103],{},[75,98,99],{},"メール送信",[75,101,102],{},"Resend",[75,104,105],{},"SendGrid, Mailgun",[41,107],{},[44,109,111],{"id":110},"_1-認証authentication-better-auth","1. 認証（Authentication）: Better Auth",[113,114,115],"h3",{"id":115},"選定理由",[14,117,118],{},"Better Auth は以下の特徴を持つ、TypeScript 向けの包括的な認証フレームワークです。",[120,121,122,129,135,141,147],"ul",{},[123,124,125,128],"li",{},[17,126,127],{},"フレームワーク非依存",": React, Vue, Svelte, Astro, Hono など多くのフレームワークに対応",[123,130,131,134],{},[17,132,133],{},"豊富な機能が標準搭載",": 2FA, パスキー, マルチテナント, セッション管理など",[123,136,137,140],{},[17,138,139],{},"プラグインエコシステム",": 機能拡張が容易",[123,142,143,146],{},[17,144,145],{},"ユーザーあたりの課金なし",": Auth0/Clerk と違い、ユーザー数に関係なく無料",[123,148,149,152],{},[17,150,151],{},"データは自分のDB",": ユーザーデータがサードパーティに渡らない",[113,154,155],{"id":155},"料金",[51,157,158,173],{},[54,159,160],{},[57,161,162,165,167,170],{},[60,163,164],{},"項目",[60,166,80],{},[60,168,169],{},"Auth0",[60,171,172],{},"Clerk",[70,174,175,191,205],{},[57,176,177,180,185,188],{},[75,178,179],{},"基本料金",[75,181,182],{},[17,183,184],{},"無料（OSS）",[75,186,187],{},"無料〜$35/月",[75,189,190],{},"無料〜$25/月",[57,192,193,196,199,202],{},[75,194,195],{},"MAU課金",[75,197,198],{},"なし",[75,200,201],{},"7,000MAU以降課金",[75,203,204],{},"10,000MAU以降課金",[57,206,207,210,213,216],{},[75,208,209],{},"セルフホスト",[75,211,212],{},"◯",[75,214,215],{},"×",[75,217,215],{},[113,219,220],{"id":220},"主要機能",[222,223,228],"pre",{"className":224,"code":226,"language":227},[225],"language-text","✅ メール/パスワード認証\n✅ ソーシャルログイン（Google, GitHub, Discord, Twitter など）\n✅ 2要素認証（2FA）\n✅ パスキー / WebAuthn\n✅ マジックリンク\n✅ セッション管理\n✅ マルチテナント / 組織管理\n✅ レート制限\n","text",[229,230,226],"code",{"__ignoreMap":231},"",[113,233,235],{"id":234},"実装例cloudflare-workers-hono","実装例（Cloudflare Workers + Hono）",[222,237,241],{"className":238,"code":239,"language":240,"meta":231,"style":231},"language-typescript shiki shiki-themes vitesse-light vitesse-light","// auth.ts\nimport { betterAuth } from \"better-auth\";\nimport { D1Adapter } from \"better-auth/adapters/d1\";\n\nexport const auth = betterAuth({\n  database: D1Adapter(env.DB),\n  emailAndPassword: {\n    enabled: true,\n  },\n  socialProviders: {\n    google: {\n      clientId: env.GOOGLE_CLIENT_ID,\n      clientSecret: env.GOOGLE_CLIENT_SECRET,\n    },\n  },\n});\n","typescript",[229,242,243,252,287,310,317,339,366,375,389,395,403,411,428,445,451,456],{"__ignoreMap":231},[244,245,248],"span",{"class":246,"line":247},"line",1,[244,249,251],{"class":250},"sxvE3","// auth.ts\n",[244,253,255,259,263,267,270,273,277,281,284],{"class":246,"line":254},2,[244,256,258],{"class":257},"sHkkW","import",[244,260,262],{"class":261},"shFtX"," {",[244,264,266],{"class":265},"s4oTP"," betterAuth",[244,268,269],{"class":261}," }",[244,271,272],{"class":257}," from",[244,274,276],{"class":275},"sMJiu"," \"",[244,278,280],{"class":279},"sdGka","better-auth",[244,282,283],{"class":275},"\"",[244,285,286],{"class":261},";\n",[244,288,290,292,294,297,299,301,303,306,308],{"class":246,"line":289},3,[244,291,258],{"class":257},[244,293,262],{"class":261},[244,295,296],{"class":265}," D1Adapter",[244,298,269],{"class":261},[244,300,272],{"class":257},[244,302,276],{"class":275},[244,304,305],{"class":279},"better-auth/adapters/d1",[244,307,283],{"class":275},[244,309,286],{"class":261},[244,311,313],{"class":246,"line":312},4,[244,314,316],{"emptyLinePlaceholder":315},true,"\n",[244,318,320,323,327,330,333,336],{"class":246,"line":319},5,[244,321,322],{"class":257},"export",[244,324,326],{"class":325},"stQ0i"," const ",[244,328,329],{"class":265},"auth",[244,331,332],{"class":261}," =",[244,334,266],{"class":335},"senZ8",[244,337,338],{"class":261},"({\n",[244,340,342,346,348,351,354,357,360,363],{"class":246,"line":341},6,[244,343,345],{"class":344},"sz8Xr","  database",[244,347,20],{"class":261},[244,349,350],{"class":335},"D1Adapter",[244,352,353],{"class":261},"(",[244,355,356],{"class":265},"env",[244,358,359],{"class":261},".",[244,361,362],{"class":265},"DB",[244,364,365],{"class":261},"),\n",[244,367,369,372],{"class":246,"line":368},7,[244,370,371],{"class":344},"  emailAndPassword",[244,373,374],{"class":261},": {\n",[244,376,378,381,383,386],{"class":246,"line":377},8,[244,379,380],{"class":344},"    enabled",[244,382,20],{"class":261},[244,384,385],{"class":257},"true",[244,387,388],{"class":261},",\n",[244,390,392],{"class":246,"line":391},9,[244,393,394],{"class":261},"  },\n",[244,396,398,401],{"class":246,"line":397},10,[244,399,400],{"class":344},"  socialProviders",[244,402,374],{"class":261},[244,404,406,409],{"class":246,"line":405},11,[244,407,408],{"class":344},"    google",[244,410,374],{"class":261},[244,412,414,417,419,421,423,426],{"class":246,"line":413},12,[244,415,416],{"class":344},"      clientId",[244,418,20],{"class":261},[244,420,356],{"class":265},[244,422,359],{"class":261},[244,424,425],{"class":265},"GOOGLE_CLIENT_ID",[244,427,388],{"class":261},[244,429,431,434,436,438,440,443],{"class":246,"line":430},13,[244,432,433],{"class":344},"      clientSecret",[244,435,20],{"class":261},[244,437,356],{"class":265},[244,439,359],{"class":261},[244,441,442],{"class":265},"GOOGLE_CLIENT_SECRET",[244,444,388],{"class":261},[244,446,448],{"class":246,"line":447},14,[244,449,450],{"class":261},"    },\n",[244,452,454],{"class":246,"line":453},15,[244,455,394],{"class":261},[244,457,459],{"class":246,"line":458},16,[244,460,461],{"class":261},"});\n",[222,463,465],{"className":238,"code":464,"language":240,"meta":231,"style":231},"// クライアント側\nimport { createAuthClient } from \"better-auth/client\";\n\nexport const authClient = createAuthClient({\n  baseURL: \"https://chat-story.number55number55.workers.dev\",\n});\n\n// サインアップ\nawait authClient.signUp.email({\n  email: \"user@example.com\",\n  password: \"securepassword\",\n  name: \"User Name\",\n});\n\n// サインイン\nawait authClient.signIn.email({\n  email: \"user@example.com\",\n  password: \"securepassword\",\n});\n",[229,466,467,472,494,498,513,529,533,537,542,562,578,594,610,614,618,623,640,655,670],{"__ignoreMap":231},[244,468,469],{"class":246,"line":247},[244,470,471],{"class":250},"// クライアント側\n",[244,473,474,476,478,481,483,485,487,490,492],{"class":246,"line":254},[244,475,258],{"class":257},[244,477,262],{"class":261},[244,479,480],{"class":265}," createAuthClient",[244,482,269],{"class":261},[244,484,272],{"class":257},[244,486,276],{"class":275},[244,488,489],{"class":279},"better-auth/client",[244,491,283],{"class":275},[244,493,286],{"class":261},[244,495,496],{"class":246,"line":289},[244,497,316],{"emptyLinePlaceholder":315},[244,499,500,502,504,507,509,511],{"class":246,"line":312},[244,501,322],{"class":257},[244,503,326],{"class":325},[244,505,506],{"class":265},"authClient",[244,508,332],{"class":261},[244,510,480],{"class":335},[244,512,338],{"class":261},[244,514,515,518,520,522,525,527],{"class":246,"line":319},[244,516,517],{"class":344},"  baseURL",[244,519,20],{"class":261},[244,521,283],{"class":275},[244,523,524],{"class":279},"https://chat-story.number55number55.workers.dev",[244,526,283],{"class":275},[244,528,388],{"class":261},[244,530,531],{"class":246,"line":341},[244,532,461],{"class":261},[244,534,535],{"class":246,"line":368},[244,536,316],{"emptyLinePlaceholder":315},[244,538,539],{"class":246,"line":377},[244,540,541],{"class":250},"// サインアップ\n",[244,543,544,547,550,552,555,557,560],{"class":246,"line":391},[244,545,546],{"class":257},"await",[244,548,549],{"class":265}," authClient",[244,551,359],{"class":261},[244,553,554],{"class":265},"signUp",[244,556,359],{"class":261},[244,558,559],{"class":335},"email",[244,561,338],{"class":261},[244,563,564,567,569,571,574,576],{"class":246,"line":397},[244,565,566],{"class":344},"  email",[244,568,20],{"class":261},[244,570,283],{"class":275},[244,572,573],{"class":279},"user@example.com",[244,575,283],{"class":275},[244,577,388],{"class":261},[244,579,580,583,585,587,590,592],{"class":246,"line":405},[244,581,582],{"class":344},"  password",[244,584,20],{"class":261},[244,586,283],{"class":275},[244,588,589],{"class":279},"securepassword",[244,591,283],{"class":275},[244,593,388],{"class":261},[244,595,596,599,601,603,606,608],{"class":246,"line":413},[244,597,598],{"class":344},"  name",[244,600,20],{"class":261},[244,602,283],{"class":275},[244,604,605],{"class":279},"User Name",[244,607,283],{"class":275},[244,609,388],{"class":261},[244,611,612],{"class":246,"line":430},[244,613,461],{"class":261},[244,615,616],{"class":246,"line":447},[244,617,316],{"emptyLinePlaceholder":315},[244,619,620],{"class":246,"line":453},[244,621,622],{"class":250},"// サインイン\n",[244,624,625,627,629,631,634,636,638],{"class":246,"line":458},[244,626,546],{"class":257},[244,628,549],{"class":265},[244,630,359],{"class":261},[244,632,633],{"class":265},"signIn",[244,635,359],{"class":261},[244,637,559],{"class":335},[244,639,338],{"class":261},[244,641,643,645,647,649,651,653],{"class":246,"line":642},17,[244,644,566],{"class":344},[244,646,20],{"class":261},[244,648,283],{"class":275},[244,650,573],{"class":279},[244,652,283],{"class":275},[244,654,388],{"class":261},[244,656,658,660,662,664,666,668],{"class":246,"line":657},18,[244,659,582],{"class":344},[244,661,20],{"class":261},[244,663,283],{"class":275},[244,665,589],{"class":279},[244,667,283],{"class":275},[244,669,388],{"class":261},[244,671,673],{"class":246,"line":672},19,[244,674,461],{"class":261},[113,676,677],{"id":677},"セットアップ手順",[679,680,681,702,730],"ol",{},[123,682,683,684],{},"パッケージインストール",[222,685,689],{"className":686,"code":687,"language":688,"meta":231,"style":231},"language-bash shiki shiki-themes vitesse-light vitesse-light","npm install better-auth\n","bash",[229,690,691],{"__ignoreMap":231},[244,692,693,696,699],{"class":246,"line":247},[244,694,695],{"class":335},"npm",[244,697,698],{"class":279}," install",[244,700,701],{"class":279}," better-auth\n",[123,703,704,705],{},"DBスキーマ生成",[222,706,708],{"className":686,"code":707,"language":688,"meta":231,"style":231},"npx @better-auth/cli generate\nnpx @better-auth/cli migrate\n",[229,709,710,721],{"__ignoreMap":231},[244,711,712,715,718],{"class":246,"line":247},[244,713,714],{"class":335},"npx",[244,716,717],{"class":279}," @better-auth/cli",[244,719,720],{"class":279}," generate\n",[244,722,723,725,727],{"class":246,"line":254},[244,724,714],{"class":335},[244,726,717],{"class":279},[244,728,729],{"class":279}," migrate\n",[123,731,732,733],{},"環境変数設定",[222,734,737],{"className":735,"code":736,"language":356,"meta":231,"style":231},"language-env shiki shiki-themes vitesse-light vitesse-light","BETTER_AUTH_SECRET=your-secret-key\nGOOGLE_CLIENT_ID=xxx\nGOOGLE_CLIENT_SECRET=xxx\n",[229,738,739,744,749],{"__ignoreMap":231},[244,740,741],{"class":246,"line":247},[244,742,743],{},"BETTER_AUTH_SECRET=your-secret-key\n",[244,745,746],{"class":246,"line":254},[244,747,748],{},"GOOGLE_CLIENT_ID=xxx\n",[244,750,751],{"class":246,"line":289},[244,752,753],{},"GOOGLE_CLIENT_SECRET=xxx\n",[113,755,756],{"id":756},"参考リンク",[120,758,759,766,773],{},[123,760,761,762],{},"公式ドキュメント: ",[22,763,764],{"href":764,"rel":765},"https://www.better-auth.com/docs",[26],[123,767,768,769],{},"GitHub: ",[22,770,771],{"href":771,"rel":772},"https://github.com/better-auth/better-auth",[26],[123,774,775,776],{},"LLMs.txt（AI向け）: ",[22,777,778],{"href":778,"rel":779},"https://better-auth.com/llms.txt",[26],[41,781],{},[44,783,785],{"id":784},"_2-決済payment-polarsh","2. 決済（Payment）: Polar.sh",[113,787,789],{"id":788},"stripe-vs-mormerchant-of-recordの違い","Stripe vs MoR（Merchant of Record）の違い",[14,791,792,793,796,797,800],{},"決済システムを選ぶ際、最も重要な判断ポイントは ",[17,794,795],{},"Stripe（決済代行）"," か ",[17,798,799],{},"MoR（販売代行）"," かの選択です。",[51,802,803,814],{},[54,804,805],{},[57,806,807,809,811],{},[60,808,164],{},[60,810,795],{},[60,812,813],{},"Polar.sh / Creem（MoR）",[70,815,816,829,844,857,870,882],{},[57,817,818,823,826],{},[75,819,820],{},[17,821,822],{},"法的な販売者",[75,824,825],{},"あなた",[75,827,828],{},"MoR事業者",[57,830,831,836,839],{},[75,832,833],{},[17,834,835],{},"税務処理（VAT/GST）",[75,837,838],{},"自分で申告・納付",[75,840,841],{},[17,842,843],{},"自動で全世界対応",[57,845,846,851,854],{},[75,847,848],{},[17,849,850],{},"手数料",[75,852,853],{},"約3.6%",[75,855,856],{},"約4% + 40¢",[57,858,859,864,867],{},[75,860,861],{},[17,862,863],{},"請求書発行",[75,865,866],{},"自分で対応",[75,868,869],{},"自動発行",[57,871,872,877,879],{},[75,873,874],{},[17,875,876],{},"チャージバック対応",[75,878,866],{},[75,880,881],{},"MoRが対応",[57,883,884,889,891],{},[75,885,886],{},[17,887,888],{},"コンプライアンス",[75,890,866],{},[75,892,881],{},[113,894,896],{"id":895},"推奨-polarsh","推奨: Polar.sh",[898,899,115],"h4",{"id":900},"選定理由-1",[679,902,903,909,915,935,941],{},[123,904,905,908],{},[17,906,907],{},"オープンソース","（Apache 2.0）で透明性が高い",[123,910,911,914],{},[17,912,913],{},"手数料が安い",": 4% + 40¢（従来MoRより20%安い）",[123,916,917,920,921],{},[17,918,919],{},"開発者向け機能が充実",":\n",[120,922,923,926,929,932],{},[123,924,925],{},"ライセンスキー自動発行",[123,927,928],{},"GitHubリポジトリアクセス管理",[123,930,931],{},"Discordロール自動付与",[123,933,934],{},"ファイルダウンロード配信",[123,936,937,940],{},[17,938,939],{},"DXが優秀",": API/SDK設計が洗練されている",[123,942,943,946],{},[17,944,945],{},"$10M調達済み","（Accel主導）で信頼性あり",[898,948,949],{"id":949},"料金体系",[222,951,954],{"className":952,"code":953,"language":227},[225],"基本手数料: 4% + 40¢ / 取引\n- 追加の国際カード手数料: なし\n- 月額固定費: なし\n- 初期費用: なし\n",[229,955,953],{"__ignoreMap":231},[898,957,220],{"id":958},"主要機能-1",[222,960,963],{"className":961,"code":962,"language":227},[225],"✅ サブスクリプション（定期課金）\n✅ 単発購入\n✅ 使用量ベース課金（Usage-based billing）\n✅ グローバル税務コンプライアンス（VAT/GST/消費税）\n✅ ライセンスキー発行\n✅ ファイル配信（最大10GB）\n✅ 顧客ポータル\n✅ Webhook\n✅ 分析ダッシュボード\n",[229,964,962],{"__ignoreMap":231},[113,966,967],{"id":967},"実装例",[222,969,971],{"className":238,"code":970,"language":240,"meta":231,"style":231},"// サーバー側: チェックアウトセッション作成\nimport { Polar } from \"@polar-sh/sdk\";\n\nconst polar = new Polar({\n  accessToken: env.POLAR_ACCESS_TOKEN,\n});\n\n// チェックアウトURL生成\nconst checkout = await polar.checkouts.create({\n  productId: \"prod_xxx\",\n  successUrl: \"https://chat-story.../success\",\n  customerEmail: user.email,\n});\n\nreturn Response.redirect(checkout.url);\n",[229,972,973,978,1000,1004,1022,1038,1042,1046,1051,1078,1094,1110,1126,1130,1134],{"__ignoreMap":231},[244,974,975],{"class":246,"line":247},[244,976,977],{"class":250},"// サーバー側: チェックアウトセッション作成\n",[244,979,980,982,984,987,989,991,993,996,998],{"class":246,"line":254},[244,981,258],{"class":257},[244,983,262],{"class":261},[244,985,986],{"class":265}," Polar",[244,988,269],{"class":261},[244,990,272],{"class":257},[244,992,276],{"class":275},[244,994,995],{"class":279},"@polar-sh/sdk",[244,997,283],{"class":275},[244,999,286],{"class":261},[244,1001,1002],{"class":246,"line":289},[244,1003,316],{"emptyLinePlaceholder":315},[244,1005,1006,1009,1012,1014,1017,1020],{"class":246,"line":312},[244,1007,1008],{"class":325},"const ",[244,1010,1011],{"class":265},"polar",[244,1013,332],{"class":261},[244,1015,1016],{"class":325}," new ",[244,1018,1019],{"class":335},"Polar",[244,1021,338],{"class":261},[244,1023,1024,1027,1029,1031,1033,1036],{"class":246,"line":319},[244,1025,1026],{"class":344},"  accessToken",[244,1028,20],{"class":261},[244,1030,356],{"class":265},[244,1032,359],{"class":261},[244,1034,1035],{"class":265},"POLAR_ACCESS_TOKEN",[244,1037,388],{"class":261},[244,1039,1040],{"class":246,"line":341},[244,1041,461],{"class":261},[244,1043,1044],{"class":246,"line":368},[244,1045,316],{"emptyLinePlaceholder":315},[244,1047,1048],{"class":246,"line":377},[244,1049,1050],{"class":250},"// チェックアウトURL生成\n",[244,1052,1053,1055,1058,1060,1063,1066,1068,1071,1073,1076],{"class":246,"line":391},[244,1054,1008],{"class":325},[244,1056,1057],{"class":265},"checkout",[244,1059,332],{"class":261},[244,1061,1062],{"class":257}," await",[244,1064,1065],{"class":265}," polar",[244,1067,359],{"class":261},[244,1069,1070],{"class":265},"checkouts",[244,1072,359],{"class":261},[244,1074,1075],{"class":335},"create",[244,1077,338],{"class":261},[244,1079,1080,1083,1085,1087,1090,1092],{"class":246,"line":397},[244,1081,1082],{"class":344},"  productId",[244,1084,20],{"class":261},[244,1086,283],{"class":275},[244,1088,1089],{"class":279},"prod_xxx",[244,1091,283],{"class":275},[244,1093,388],{"class":261},[244,1095,1096,1099,1101,1103,1106,1108],{"class":246,"line":405},[244,1097,1098],{"class":344},"  successUrl",[244,1100,20],{"class":261},[244,1102,283],{"class":275},[244,1104,1105],{"class":279},"https://chat-story.../success",[244,1107,283],{"class":275},[244,1109,388],{"class":261},[244,1111,1112,1115,1117,1120,1122,1124],{"class":246,"line":413},[244,1113,1114],{"class":344},"  customerEmail",[244,1116,20],{"class":261},[244,1118,1119],{"class":265},"user",[244,1121,359],{"class":261},[244,1123,559],{"class":265},[244,1125,388],{"class":261},[244,1127,1128],{"class":246,"line":430},[244,1129,461],{"class":261},[244,1131,1132],{"class":246,"line":447},[244,1133,316],{"emptyLinePlaceholder":315},[244,1135,1136,1139,1142,1144,1147,1149,1151,1153,1156],{"class":246,"line":453},[244,1137,1138],{"class":257},"return",[244,1140,1141],{"class":265}," Response",[244,1143,359],{"class":261},[244,1145,1146],{"class":335},"redirect",[244,1148,353],{"class":261},[244,1150,1057],{"class":265},[244,1152,359],{"class":261},[244,1154,1155],{"class":265},"url",[244,1157,1158],{"class":261},");\n",[222,1160,1162],{"className":238,"code":1161,"language":240,"meta":231,"style":231},"// Webhook処理\napp.post(\"/webhook/polar\", async (c) => {\n  const payload = await c.req.json();\n  const signature = c.req.header(\"polar-signature\");\n  \n  // 署名検証\n  const event = polar.webhooks.constructEvent(payload, signature, env.POLAR_WEBHOOK_SECRET);\n  \n  switch (event.type) {\n    case \"subscription.created\":\n      // サブスクリプション開始処理\n      await activatePremium(event.data.customer.email);\n      break;\n    case \"subscription.canceled\":\n      // サブスクリプション解約処理\n      await deactivatePremium(event.data.customer.email);\n      break;\n  }\n  \n  return c.json({ received: true });\n});\n",[229,1163,1164,1169,1209,1237,1268,1274,1279,1321,1325,1343,1357,1362,1390,1397,1410,1415,1440,1446,1451,1455,1480],{"__ignoreMap":231},[244,1165,1166],{"class":246,"line":247},[244,1167,1168],{"class":250},"// Webhook処理\n",[244,1170,1171,1174,1176,1179,1181,1183,1186,1188,1191,1194,1197,1200,1203,1206],{"class":246,"line":254},[244,1172,1173],{"class":265},"app",[244,1175,359],{"class":261},[244,1177,1178],{"class":335},"post",[244,1180,353],{"class":261},[244,1182,283],{"class":275},[244,1184,1185],{"class":279},"/webhook/polar",[244,1187,283],{"class":275},[244,1189,1190],{"class":261},",",[244,1192,1193],{"class":325}," async",[244,1195,1196],{"class":261}," (",[244,1198,1199],{"class":265},"c",[244,1201,1202],{"class":261},")",[244,1204,1205],{"class":261}," =>",[244,1207,1208],{"class":261}," {\n",[244,1210,1211,1214,1217,1219,1221,1224,1226,1229,1231,1234],{"class":246,"line":289},[244,1212,1213],{"class":325},"  const ",[244,1215,1216],{"class":265},"payload",[244,1218,332],{"class":261},[244,1220,1062],{"class":257},[244,1222,1223],{"class":265}," c",[244,1225,359],{"class":261},[244,1227,1228],{"class":265},"req",[244,1230,359],{"class":261},[244,1232,1233],{"class":335},"json",[244,1235,1236],{"class":261},"();\n",[244,1238,1239,1241,1244,1246,1248,1250,1252,1254,1257,1259,1261,1264,1266],{"class":246,"line":312},[244,1240,1213],{"class":325},[244,1242,1243],{"class":265},"signature",[244,1245,332],{"class":261},[244,1247,1223],{"class":265},[244,1249,359],{"class":261},[244,1251,1228],{"class":265},[244,1253,359],{"class":261},[244,1255,1256],{"class":335},"header",[244,1258,353],{"class":261},[244,1260,283],{"class":275},[244,1262,1263],{"class":279},"polar-signature",[244,1265,283],{"class":275},[244,1267,1158],{"class":261},[244,1269,1270],{"class":246,"line":319},[244,1271,1273],{"class":1272},"sG7-3","  \n",[244,1275,1276],{"class":246,"line":341},[244,1277,1278],{"class":250},"  // 署名検証\n",[244,1280,1281,1283,1286,1288,1290,1292,1295,1297,1300,1302,1304,1306,1309,1311,1314,1316,1319],{"class":246,"line":368},[244,1282,1213],{"class":325},[244,1284,1285],{"class":265},"event",[244,1287,332],{"class":261},[244,1289,1065],{"class":265},[244,1291,359],{"class":261},[244,1293,1294],{"class":265},"webhooks",[244,1296,359],{"class":261},[244,1298,1299],{"class":335},"constructEvent",[244,1301,353],{"class":261},[244,1303,1216],{"class":265},[244,1305,1190],{"class":261},[244,1307,1308],{"class":265}," signature",[244,1310,1190],{"class":261},[244,1312,1313],{"class":265}," env",[244,1315,359],{"class":261},[244,1317,1318],{"class":265},"POLAR_WEBHOOK_SECRET",[244,1320,1158],{"class":261},[244,1322,1323],{"class":246,"line":377},[244,1324,1273],{"class":1272},[244,1326,1327,1330,1332,1334,1336,1339,1341],{"class":246,"line":391},[244,1328,1329],{"class":257},"  switch",[244,1331,1196],{"class":261},[244,1333,1285],{"class":265},[244,1335,359],{"class":261},[244,1337,1338],{"class":265},"type",[244,1340,1202],{"class":261},[244,1342,1208],{"class":261},[244,1344,1345,1348,1350,1353,1355],{"class":246,"line":397},[244,1346,1347],{"class":257},"    case",[244,1349,276],{"class":275},[244,1351,1352],{"class":279},"subscription.created",[244,1354,283],{"class":275},[244,1356,920],{"class":261},[244,1358,1359],{"class":246,"line":405},[244,1360,1361],{"class":250},"      // サブスクリプション開始処理\n",[244,1363,1364,1367,1370,1372,1374,1376,1379,1381,1384,1386,1388],{"class":246,"line":413},[244,1365,1366],{"class":257},"      await",[244,1368,1369],{"class":335}," activatePremium",[244,1371,353],{"class":261},[244,1373,1285],{"class":265},[244,1375,359],{"class":261},[244,1377,1378],{"class":265},"data",[244,1380,359],{"class":261},[244,1382,1383],{"class":265},"customer",[244,1385,359],{"class":261},[244,1387,559],{"class":265},[244,1389,1158],{"class":261},[244,1391,1392,1395],{"class":246,"line":430},[244,1393,1394],{"class":257},"      break",[244,1396,286],{"class":261},[244,1398,1399,1401,1403,1406,1408],{"class":246,"line":447},[244,1400,1347],{"class":257},[244,1402,276],{"class":275},[244,1404,1405],{"class":279},"subscription.canceled",[244,1407,283],{"class":275},[244,1409,920],{"class":261},[244,1411,1412],{"class":246,"line":453},[244,1413,1414],{"class":250},"      // サブスクリプション解約処理\n",[244,1416,1417,1419,1422,1424,1426,1428,1430,1432,1434,1436,1438],{"class":246,"line":458},[244,1418,1366],{"class":257},[244,1420,1421],{"class":335}," deactivatePremium",[244,1423,353],{"class":261},[244,1425,1285],{"class":265},[244,1427,359],{"class":261},[244,1429,1378],{"class":265},[244,1431,359],{"class":261},[244,1433,1383],{"class":265},[244,1435,359],{"class":261},[244,1437,559],{"class":265},[244,1439,1158],{"class":261},[244,1441,1442,1444],{"class":246,"line":642},[244,1443,1394],{"class":257},[244,1445,286],{"class":261},[244,1447,1448],{"class":246,"line":657},[244,1449,1450],{"class":261},"  }\n",[244,1452,1453],{"class":246,"line":672},[244,1454,1273],{"class":1272},[244,1456,1458,1461,1463,1465,1467,1470,1473,1475,1477],{"class":246,"line":1457},20,[244,1459,1460],{"class":257},"  return",[244,1462,1223],{"class":265},[244,1464,359],{"class":261},[244,1466,1233],{"class":335},[244,1468,1469],{"class":261},"({ ",[244,1471,1472],{"class":344},"received",[244,1474,20],{"class":261},[244,1476,385],{"class":257},[244,1478,1479],{"class":261}," });\n",[244,1481,1483],{"class":246,"line":1482},21,[244,1484,461],{"class":261},[113,1486,1488],{"id":1487},"代替選択肢-creemio","代替選択肢: Creem.io",[14,1490,1491],{},"Polar.sh が合わない場合の代替として Creem.io も検討可能です。",[51,1493,1494,1505],{},[54,1495,1496],{},[57,1497,1498,1500,1502],{},[60,1499,164],{},[60,1501,91],{},[60,1503,1504],{},"Creem.io",[70,1506,1507,1517,1525,1535,1546],{},[57,1508,1509,1511,1514],{},[75,1510,850],{},[75,1512,1513],{},"4% + 40¢",[75,1515,1516],{},"3.9% + 40¢",[57,1518,1519,1521,1523],{},[75,1520,907],{},[75,1522,212],{},[75,1524,215],{},[57,1526,1527,1530,1532],{},[75,1528,1529],{},"PMF前無料",[75,1531,215],{},[75,1533,1534],{},"◯（0%手数料）",[57,1536,1537,1540,1543],{},[75,1538,1539],{},"主なターゲット",[75,1541,1542],{},"開発者・OSS",[75,1544,1545],{},"SaaS全般",[57,1547,1548,1551,1554],{},[75,1549,1550],{},"設立",[75,1552,1553],{},"2023年",[75,1555,1556],{},"2024年",[113,1558,756],{"id":1559},"参考リンク-1",[120,1561,1562,1569,1576],{},[123,1563,1564,1565],{},"Polar.sh: ",[22,1566,1567],{"href":1567,"rel":1568},"https://polar.sh",[26],[123,1570,1571,1572],{},"Polar Docs: ",[22,1573,1574],{"href":1574,"rel":1575},"https://docs.polar.sh",[26],[123,1577,1578,1579],{},"Creem.io: ",[22,1580,1581],{"href":1581,"rel":1582},"https://creem.io",[26],[41,1584],{},[44,1586,1588],{"id":1587},"_3-メール送信-resend","3. メール送信: Resend",[113,1590,115],{"id":1591},"選定理由-2",[14,1593,1594],{},"Resendは開発者向けに設計されたモダンなメールAPIサービスです。",[679,1596,1597,1603,1609,1615,1621],{},[123,1598,1599,1602],{},[17,1600,1601],{},"React Email統合",": Reactコンポーネントでメールテンプレート作成",[123,1604,1605,1608],{},[17,1606,1607],{},"優れたDX",": シンプルなAPI、充実したSDK",[123,1610,1611,1614],{},[17,1612,1613],{},"高い到達率",": SPF/DKIM/DMARC対応、専用IPオプション",[123,1616,1617,1620],{},[17,1618,1619],{},"リアルタイム追跡",": 開封、クリック、バウンスのWebhook通知",[123,1622,1623,1626],{},[17,1624,1625],{},"無料枠が充実",": 月3,000通まで無料",[113,1628,949],{"id":1629},"料金体系-1",[51,1631,1632,1648],{},[54,1633,1634],{},[57,1635,1636,1639,1642,1645],{},[60,1637,1638],{},"プラン",[60,1640,1641],{},"月額",[60,1643,1644],{},"送信数",[60,1646,1647],{},"特徴",[70,1649,1650,1666,1680],{},[57,1651,1652,1657,1660,1663],{},[75,1653,1654],{},[17,1655,1656],{},"Free",[75,1658,1659],{},"$0",[75,1661,1662],{},"3,000通/月",[75,1664,1665],{},"開発・小規模向け",[57,1667,1668,1671,1674,1677],{},[75,1669,1670],{},"Pro",[75,1672,1673],{},"$20",[75,1675,1676],{},"50,000通/月",[75,1678,1679],{},"SSO対応",[57,1681,1682,1685,1688,1691],{},[75,1683,1684],{},"Scale",[75,1686,1687],{},"$100",[75,1689,1690],{},"200,000通/月",[75,1692,1693],{},"専用IP（オプション）",[113,1695,1697],{"id":1696},"chat-story-での活用シーン","Chat Story での活用シーン",[222,1699,1702],{"className":1700,"code":1701,"language":227},[225],"📧 ウェルカムメール（新規登録時）\n🔐 パスワードリセット\n🔔 サブスクリプション開始/解約通知\n📊 利用状況レポート\n💳 決済完了/失敗通知\n",[229,1703,1701],{"__ignoreMap":231},[113,1705,967],{"id":1706},"実装例-1",[898,1708,1709],{"id":1709},"基本的なメール送信",[222,1711,1713],{"className":238,"code":1712,"language":240,"meta":231,"style":231},"import { Resend } from \"resend\";\n\nconst resend = new Resend(env.RESEND_API_KEY);\n\n// シンプルなメール送信\nawait resend.emails.send({\n  from: \"Chat Story \u003Cnoreply@chat-story.com>\",\n  to: user.email,\n  subject: \"Chat Storyへようこそ！\",\n  html: `\n    \u003Ch1>ご登録ありがとうございます\u003C/h1>\n    \u003Cp>${user.name}さん、Chat Storyへようこそ！\u003C/p>\n  `,\n});\n",[229,1714,1715,1737,1741,1764,1768,1773,1792,1808,1823,1839,1849,1854,1875,1882],{"__ignoreMap":231},[244,1716,1717,1719,1721,1724,1726,1728,1730,1733,1735],{"class":246,"line":247},[244,1718,258],{"class":257},[244,1720,262],{"class":261},[244,1722,1723],{"class":265}," Resend",[244,1725,269],{"class":261},[244,1727,272],{"class":257},[244,1729,276],{"class":275},[244,1731,1732],{"class":279},"resend",[244,1734,283],{"class":275},[244,1736,286],{"class":261},[244,1738,1739],{"class":246,"line":254},[244,1740,316],{"emptyLinePlaceholder":315},[244,1742,1743,1745,1747,1749,1751,1753,1755,1757,1759,1762],{"class":246,"line":289},[244,1744,1008],{"class":325},[244,1746,1732],{"class":265},[244,1748,332],{"class":261},[244,1750,1016],{"class":325},[244,1752,102],{"class":335},[244,1754,353],{"class":261},[244,1756,356],{"class":265},[244,1758,359],{"class":261},[244,1760,1761],{"class":265},"RESEND_API_KEY",[244,1763,1158],{"class":261},[244,1765,1766],{"class":246,"line":312},[244,1767,316],{"emptyLinePlaceholder":315},[244,1769,1770],{"class":246,"line":319},[244,1771,1772],{"class":250},"// シンプルなメール送信\n",[244,1774,1775,1777,1780,1782,1785,1787,1790],{"class":246,"line":341},[244,1776,546],{"class":257},[244,1778,1779],{"class":265}," resend",[244,1781,359],{"class":261},[244,1783,1784],{"class":265},"emails",[244,1786,359],{"class":261},[244,1788,1789],{"class":335},"send",[244,1791,338],{"class":261},[244,1793,1794,1797,1799,1801,1804,1806],{"class":246,"line":368},[244,1795,1796],{"class":344},"  from",[244,1798,20],{"class":261},[244,1800,283],{"class":275},[244,1802,1803],{"class":279},"Chat Story \u003Cnoreply@chat-story.com>",[244,1805,283],{"class":275},[244,1807,388],{"class":261},[244,1809,1810,1813,1815,1817,1819,1821],{"class":246,"line":377},[244,1811,1812],{"class":344},"  to",[244,1814,20],{"class":261},[244,1816,1119],{"class":265},[244,1818,359],{"class":261},[244,1820,559],{"class":265},[244,1822,388],{"class":261},[244,1824,1825,1828,1830,1832,1835,1837],{"class":246,"line":391},[244,1826,1827],{"class":344},"  subject",[244,1829,20],{"class":261},[244,1831,283],{"class":275},[244,1833,1834],{"class":279},"Chat Storyへようこそ！",[244,1836,283],{"class":275},[244,1838,388],{"class":261},[244,1840,1841,1844,1846],{"class":246,"line":397},[244,1842,1843],{"class":344},"  html",[244,1845,20],{"class":261},[244,1847,1848],{"class":275},"`\n",[244,1850,1851],{"class":246,"line":405},[244,1852,1853],{"class":279},"    \u003Ch1>ご登録ありがとうございます\u003C/h1>\n",[244,1855,1856,1859,1862,1864,1866,1869,1872],{"class":246,"line":413},[244,1857,1858],{"class":279},"    \u003Cp>",[244,1860,1861],{"class":257},"${",[244,1863,1119],{"class":279},[244,1865,359],{"class":261},[244,1867,1868],{"class":279},"name",[244,1870,1871],{"class":257},"}",[244,1873,1874],{"class":279},"さん、Chat Storyへようこそ！\u003C/p>\n",[244,1876,1877,1880],{"class":246,"line":430},[244,1878,1879],{"class":275},"  `",[244,1881,388],{"class":261},[244,1883,1884],{"class":246,"line":447},[244,1885,461],{"class":261},[898,1887,1889],{"id":1888},"react-emailでテンプレート作成","React Emailでテンプレート作成",[222,1891,1895],{"className":1892,"code":1893,"language":1894,"meta":231,"style":231},"language-tsx shiki shiki-themes vitesse-light vitesse-light","// emails/welcome.tsx\nimport { Html, Head, Body, Container, Heading, Text, Button } from \"@react-email/components\";\n\nexport function WelcomeEmail({ name, loginUrl }: { name: string; loginUrl: string }) {\n  return (\n    \u003CHtml>\n      \u003CHead />\n      \u003CBody style={{ fontFamily: \"sans-serif\" }}>\n        \u003CContainer>\n          \u003CHeading>ようこそ、{name}さん！\u003C/Heading>\n          \u003CText>Chat Storyへのご登録ありがとうございます。\u003C/Text>\n          \u003CButton href={loginUrl} style={{ background: \"#3b82f6\", color: \"#fff\", padding: \"12px 24px\" }}>\n            ログインする\n          \u003C/Button>\n        \u003C/Container>\n      \u003C/Body>\n    \u003C/Html>\n  );\n}\n","tsx",[229,1896,1897,1902,1907,1911,1916,1921,1926,1931,1936,1941,1946,1951,1956,1961,1966,1971,1976,1981,1986],{"__ignoreMap":231},[244,1898,1899],{"class":246,"line":247},[244,1900,1901],{},"// emails/welcome.tsx\n",[244,1903,1904],{"class":246,"line":254},[244,1905,1906],{},"import { Html, Head, Body, Container, Heading, Text, Button } from \"@react-email/components\";\n",[244,1908,1909],{"class":246,"line":289},[244,1910,316],{"emptyLinePlaceholder":315},[244,1912,1913],{"class":246,"line":312},[244,1914,1915],{},"export function WelcomeEmail({ name, loginUrl }: { name: string; loginUrl: string }) {\n",[244,1917,1918],{"class":246,"line":319},[244,1919,1920],{},"  return (\n",[244,1922,1923],{"class":246,"line":341},[244,1924,1925],{},"    \u003CHtml>\n",[244,1927,1928],{"class":246,"line":368},[244,1929,1930],{},"      \u003CHead />\n",[244,1932,1933],{"class":246,"line":377},[244,1934,1935],{},"      \u003CBody style={{ fontFamily: \"sans-serif\" }}>\n",[244,1937,1938],{"class":246,"line":391},[244,1939,1940],{},"        \u003CContainer>\n",[244,1942,1943],{"class":246,"line":397},[244,1944,1945],{},"          \u003CHeading>ようこそ、{name}さん！\u003C/Heading>\n",[244,1947,1948],{"class":246,"line":405},[244,1949,1950],{},"          \u003CText>Chat Storyへのご登録ありがとうございます。\u003C/Text>\n",[244,1952,1953],{"class":246,"line":413},[244,1954,1955],{},"          \u003CButton href={loginUrl} style={{ background: \"#3b82f6\", color: \"#fff\", padding: \"12px 24px\" }}>\n",[244,1957,1958],{"class":246,"line":430},[244,1959,1960],{},"            ログインする\n",[244,1962,1963],{"class":246,"line":447},[244,1964,1965],{},"          \u003C/Button>\n",[244,1967,1968],{"class":246,"line":453},[244,1969,1970],{},"        \u003C/Container>\n",[244,1972,1973],{"class":246,"line":458},[244,1974,1975],{},"      \u003C/Body>\n",[244,1977,1978],{"class":246,"line":642},[244,1979,1980],{},"    \u003C/Html>\n",[244,1982,1983],{"class":246,"line":657},[244,1984,1985],{},"  );\n",[244,1987,1988],{"class":246,"line":672},[244,1989,1990],{},"}\n",[222,1992,1994],{"className":238,"code":1993,"language":240,"meta":231,"style":231},"// メール送信\nimport { WelcomeEmail } from \"./emails/welcome\";\nimport { render } from \"@react-email/render\";\n\nconst html = await render(WelcomeEmail({ \n  name: user.name, \n  loginUrl: \"https://chat-story.../login\" \n}));\n\nawait resend.emails.send({\n  from: \"Chat Story \u003Cnoreply@chat-story.com>\",\n  to: user.email,\n  subject: \"Chat Storyへようこそ！\",\n  html,\n});\n",[229,1995,1996,2001,2023,2045,2049,2070,2085,2102,2107,2111,2127,2141,2155,2169,2175],{"__ignoreMap":231},[244,1997,1998],{"class":246,"line":247},[244,1999,2000],{"class":250},"// メール送信\n",[244,2002,2003,2005,2007,2010,2012,2014,2016,2019,2021],{"class":246,"line":254},[244,2004,258],{"class":257},[244,2006,262],{"class":261},[244,2008,2009],{"class":265}," WelcomeEmail",[244,2011,269],{"class":261},[244,2013,272],{"class":257},[244,2015,276],{"class":275},[244,2017,2018],{"class":279},"./emails/welcome",[244,2020,283],{"class":275},[244,2022,286],{"class":261},[244,2024,2025,2027,2029,2032,2034,2036,2038,2041,2043],{"class":246,"line":289},[244,2026,258],{"class":257},[244,2028,262],{"class":261},[244,2030,2031],{"class":265}," render",[244,2033,269],{"class":261},[244,2035,272],{"class":257},[244,2037,276],{"class":275},[244,2039,2040],{"class":279},"@react-email/render",[244,2042,283],{"class":275},[244,2044,286],{"class":261},[244,2046,2047],{"class":246,"line":312},[244,2048,316],{"emptyLinePlaceholder":315},[244,2050,2051,2053,2056,2058,2060,2062,2064,2067],{"class":246,"line":319},[244,2052,1008],{"class":325},[244,2054,2055],{"class":265},"html",[244,2057,332],{"class":261},[244,2059,1062],{"class":257},[244,2061,2031],{"class":335},[244,2063,353],{"class":261},[244,2065,2066],{"class":335},"WelcomeEmail",[244,2068,2069],{"class":261},"({ \n",[244,2071,2072,2074,2076,2078,2080,2082],{"class":246,"line":341},[244,2073,598],{"class":344},[244,2075,20],{"class":261},[244,2077,1119],{"class":265},[244,2079,359],{"class":261},[244,2081,1868],{"class":265},[244,2083,2084],{"class":261},", \n",[244,2086,2087,2090,2092,2094,2097,2099],{"class":246,"line":368},[244,2088,2089],{"class":344},"  loginUrl",[244,2091,20],{"class":261},[244,2093,283],{"class":275},[244,2095,2096],{"class":279},"https://chat-story.../login",[244,2098,283],{"class":275},[244,2100,2101],{"class":261}," \n",[244,2103,2104],{"class":246,"line":377},[244,2105,2106],{"class":261},"}));\n",[244,2108,2109],{"class":246,"line":391},[244,2110,316],{"emptyLinePlaceholder":315},[244,2112,2113,2115,2117,2119,2121,2123,2125],{"class":246,"line":397},[244,2114,546],{"class":257},[244,2116,1779],{"class":265},[244,2118,359],{"class":261},[244,2120,1784],{"class":265},[244,2122,359],{"class":261},[244,2124,1789],{"class":335},[244,2126,338],{"class":261},[244,2128,2129,2131,2133,2135,2137,2139],{"class":246,"line":405},[244,2130,1796],{"class":344},[244,2132,20],{"class":261},[244,2134,283],{"class":275},[244,2136,1803],{"class":279},[244,2138,283],{"class":275},[244,2140,388],{"class":261},[244,2142,2143,2145,2147,2149,2151,2153],{"class":246,"line":413},[244,2144,1812],{"class":344},[244,2146,20],{"class":261},[244,2148,1119],{"class":265},[244,2150,359],{"class":261},[244,2152,559],{"class":265},[244,2154,388],{"class":261},[244,2156,2157,2159,2161,2163,2165,2167],{"class":246,"line":430},[244,2158,1827],{"class":344},[244,2160,20],{"class":261},[244,2162,283],{"class":275},[244,2164,1834],{"class":279},[244,2166,283],{"class":275},[244,2168,388],{"class":261},[244,2170,2171,2173],{"class":246,"line":447},[244,2172,1843],{"class":265},[244,2174,388],{"class":261},[244,2176,2177],{"class":246,"line":453},[244,2178,461],{"class":261},[898,2180,2182],{"id":2181},"better-auth-との統合","Better Auth との統合",[222,2184,2186],{"className":238,"code":2185,"language":240,"meta":231,"style":231},"// auth.ts\nimport { betterAuth } from \"better-auth\";\nimport { Resend } from \"resend\";\n\nconst resend = new Resend(env.RESEND_API_KEY);\n\nexport const auth = betterAuth({\n  emailAndPassword: {\n    enabled: true,\n    sendResetPassword: async ({ user, url }) => {\n      await resend.emails.send({\n        from: \"Chat Story \u003Cnoreply@chat-story.com>\",\n        to: user.email,\n        subject: \"パスワードリセット\",\n        html: `\u003Ca href=\"${url}\">こちらをクリックしてパスワードをリセット\u003C/a>`,\n      });\n    },\n    sendVerificationEmail: async ({ user, url }) => {\n      await resend.emails.send({\n        from: \"Chat Story \u003Cnoreply@chat-story.com>\",\n        to: user.email,\n        subject: \"メールアドレスの確認\",\n        html: `\u003Ca href=\"${url}\">こちらをクリックしてメールアドレスを確認\u003C/a>`,\n      });\n    },\n  },\n});\n",[229,2187,2188,2192,2212,2232,2236,2258,2262,2276,2282,2292,2315,2331,2346,2361,2377,2403,2408,2412,2431,2447,2461,2475,2491,2515,2520,2525,2530],{"__ignoreMap":231},[244,2189,2190],{"class":246,"line":247},[244,2191,251],{"class":250},[244,2193,2194,2196,2198,2200,2202,2204,2206,2208,2210],{"class":246,"line":254},[244,2195,258],{"class":257},[244,2197,262],{"class":261},[244,2199,266],{"class":265},[244,2201,269],{"class":261},[244,2203,272],{"class":257},[244,2205,276],{"class":275},[244,2207,280],{"class":279},[244,2209,283],{"class":275},[244,2211,286],{"class":261},[244,2213,2214,2216,2218,2220,2222,2224,2226,2228,2230],{"class":246,"line":289},[244,2215,258],{"class":257},[244,2217,262],{"class":261},[244,2219,1723],{"class":265},[244,2221,269],{"class":261},[244,2223,272],{"class":257},[244,2225,276],{"class":275},[244,2227,1732],{"class":279},[244,2229,283],{"class":275},[244,2231,286],{"class":261},[244,2233,2234],{"class":246,"line":312},[244,2235,316],{"emptyLinePlaceholder":315},[244,2237,2238,2240,2242,2244,2246,2248,2250,2252,2254,2256],{"class":246,"line":319},[244,2239,1008],{"class":325},[244,2241,1732],{"class":265},[244,2243,332],{"class":261},[244,2245,1016],{"class":325},[244,2247,102],{"class":335},[244,2249,353],{"class":261},[244,2251,356],{"class":265},[244,2253,359],{"class":261},[244,2255,1761],{"class":265},[244,2257,1158],{"class":261},[244,2259,2260],{"class":246,"line":341},[244,2261,316],{"emptyLinePlaceholder":315},[244,2263,2264,2266,2268,2270,2272,2274],{"class":246,"line":368},[244,2265,322],{"class":257},[244,2267,326],{"class":325},[244,2269,329],{"class":265},[244,2271,332],{"class":261},[244,2273,266],{"class":335},[244,2275,338],{"class":261},[244,2277,2278,2280],{"class":246,"line":377},[244,2279,371],{"class":344},[244,2281,374],{"class":261},[244,2283,2284,2286,2288,2290],{"class":246,"line":391},[244,2285,380],{"class":344},[244,2287,20],{"class":261},[244,2289,385],{"class":257},[244,2291,388],{"class":261},[244,2293,2294,2297,2299,2302,2305,2307,2310,2312],{"class":246,"line":397},[244,2295,2296],{"class":335},"    sendResetPassword",[244,2298,20],{"class":261},[244,2300,2301],{"class":325},"async",[244,2303,2304],{"class":261}," ({ ",[244,2306,1119],{"class":265},[244,2308,2309],{"class":261},", ",[244,2311,1155],{"class":265},[244,2313,2314],{"class":261}," }) => {\n",[244,2316,2317,2319,2321,2323,2325,2327,2329],{"class":246,"line":405},[244,2318,1366],{"class":257},[244,2320,1779],{"class":265},[244,2322,359],{"class":261},[244,2324,1784],{"class":265},[244,2326,359],{"class":261},[244,2328,1789],{"class":335},[244,2330,338],{"class":261},[244,2332,2333,2336,2338,2340,2342,2344],{"class":246,"line":413},[244,2334,2335],{"class":344},"        from",[244,2337,20],{"class":261},[244,2339,283],{"class":275},[244,2341,1803],{"class":279},[244,2343,283],{"class":275},[244,2345,388],{"class":261},[244,2347,2348,2351,2353,2355,2357,2359],{"class":246,"line":430},[244,2349,2350],{"class":344},"        to",[244,2352,20],{"class":261},[244,2354,1119],{"class":265},[244,2356,359],{"class":261},[244,2358,559],{"class":265},[244,2360,388],{"class":261},[244,2362,2363,2366,2368,2370,2373,2375],{"class":246,"line":447},[244,2364,2365],{"class":344},"        subject",[244,2367,20],{"class":261},[244,2369,283],{"class":275},[244,2371,2372],{"class":279},"パスワードリセット",[244,2374,283],{"class":275},[244,2376,388],{"class":261},[244,2378,2379,2382,2384,2387,2390,2392,2394,2396,2399,2401],{"class":246,"line":453},[244,2380,2381],{"class":344},"        html",[244,2383,20],{"class":261},[244,2385,2386],{"class":275},"`",[244,2388,2389],{"class":279},"\u003Ca href=\"",[244,2391,1861],{"class":257},[244,2393,1155],{"class":279},[244,2395,1871],{"class":257},[244,2397,2398],{"class":279},"\">こちらをクリックしてパスワードをリセット\u003C/a>",[244,2400,2386],{"class":275},[244,2402,388],{"class":261},[244,2404,2405],{"class":246,"line":458},[244,2406,2407],{"class":261},"      });\n",[244,2409,2410],{"class":246,"line":642},[244,2411,450],{"class":261},[244,2413,2414,2417,2419,2421,2423,2425,2427,2429],{"class":246,"line":657},[244,2415,2416],{"class":335},"    sendVerificationEmail",[244,2418,20],{"class":261},[244,2420,2301],{"class":325},[244,2422,2304],{"class":261},[244,2424,1119],{"class":265},[244,2426,2309],{"class":261},[244,2428,1155],{"class":265},[244,2430,2314],{"class":261},[244,2432,2433,2435,2437,2439,2441,2443,2445],{"class":246,"line":672},[244,2434,1366],{"class":257},[244,2436,1779],{"class":265},[244,2438,359],{"class":261},[244,2440,1784],{"class":265},[244,2442,359],{"class":261},[244,2444,1789],{"class":335},[244,2446,338],{"class":261},[244,2448,2449,2451,2453,2455,2457,2459],{"class":246,"line":1457},[244,2450,2335],{"class":344},[244,2452,20],{"class":261},[244,2454,283],{"class":275},[244,2456,1803],{"class":279},[244,2458,283],{"class":275},[244,2460,388],{"class":261},[244,2462,2463,2465,2467,2469,2471,2473],{"class":246,"line":1482},[244,2464,2350],{"class":344},[244,2466,20],{"class":261},[244,2468,1119],{"class":265},[244,2470,359],{"class":261},[244,2472,559],{"class":265},[244,2474,388],{"class":261},[244,2476,2478,2480,2482,2484,2487,2489],{"class":246,"line":2477},22,[244,2479,2365],{"class":344},[244,2481,20],{"class":261},[244,2483,283],{"class":275},[244,2485,2486],{"class":279},"メールアドレスの確認",[244,2488,283],{"class":275},[244,2490,388],{"class":261},[244,2492,2494,2496,2498,2500,2502,2504,2506,2508,2511,2513],{"class":246,"line":2493},23,[244,2495,2381],{"class":344},[244,2497,20],{"class":261},[244,2499,2386],{"class":275},[244,2501,2389],{"class":279},[244,2503,1861],{"class":257},[244,2505,1155],{"class":279},[244,2507,1871],{"class":257},[244,2509,2510],{"class":279},"\">こちらをクリックしてメールアドレスを確認\u003C/a>",[244,2512,2386],{"class":275},[244,2514,388],{"class":261},[244,2516,2518],{"class":246,"line":2517},24,[244,2519,2407],{"class":261},[244,2521,2523],{"class":246,"line":2522},25,[244,2524,450],{"class":261},[244,2526,2528],{"class":246,"line":2527},26,[244,2529,394],{"class":261},[244,2531,2533],{"class":246,"line":2532},27,[244,2534,461],{"class":261},[113,2536,677],{"id":2537},"セットアップ手順-1",[679,2539,2540,2547,2550,2553],{},[123,2541,2542,2543],{},"Resendアカウント作成: ",[22,2544,2545],{"href":2545,"rel":2546},"https://resend.com/signup",[26],[123,2548,2549],{},"ドメイン認証（DNS設定）",[123,2551,2552],{},"APIキー取得",[123,2554,2555,2556],{},"パッケージインストール\n",[222,2557,2559],{"className":686,"code":2558,"language":688,"meta":231,"style":231},"npm install resend @react-email/components\n",[229,2560,2561],{"__ignoreMap":231},[244,2562,2563,2565,2567,2569],{"class":246,"line":247},[244,2564,695],{"class":335},[244,2566,698],{"class":279},[244,2568,1779],{"class":279},[244,2570,2571],{"class":279}," @react-email/components\n",[113,2573,756],{"id":2574},"参考リンク-2",[120,2576,2577,2584,2591],{},[123,2578,2579,2580],{},"Resend: ",[22,2581,2582],{"href":2582,"rel":2583},"https://resend.com",[26],[123,2585,2586,2587],{},"React Email: ",[22,2588,2589],{"href":2589,"rel":2590},"https://react.email",[26],[123,2592,2593,2594],{},"Resend Docs: ",[22,2595,2596],{"href":2596,"rel":2597},"https://resend.com/docs",[26],[41,2599],{},[44,2601,2603],{"id":2602},"_4-実装フェーズとタスク","4. 実装フェーズとタスク",[113,2605,2607],{"id":2606},"phase-1-認証優先度-高","Phase 1: 認証（優先度: 高）",[2609,2610,2611],"blockquote",{},[14,2612,2613,2614,2618],{},"📝 Google OAuth の詳細な設定手順は ",[22,2615,2617],{"href":2616},"/google-oauth-setup-guide","Google OAuth 設定ガイド"," を参照",[222,2620,2623],{"className":2621,"code":2622,"language":227},[225],"□ Better Auth セットアップ\n  □ D1データベーススキーマ作成\n  □ 認証エンドポイント実装\n  □ クライアント側認証フック作成\n□ ログイン/サインアップUI作成\n  □ メール/パスワード\n  □ Google OAuth（オプション）→ [設定ガイド](/google-oauth-setup-guide)\n□ セッション管理\n  □ 保護されたルート実装\n  □ ログアウト処理\n",[229,2624,2622],{"__ignoreMap":231},[113,2626,2628],{"id":2627},"phase-2-メール送信優先度-中","Phase 2: メール送信（優先度: 中）",[222,2630,2633],{"className":2631,"code":2632,"language":227},[225],"□ Resend セットアップ\n  □ ドメイン認証\n  □ APIキー設定\n□ メールテンプレート作成\n  □ ウェルカムメール\n  □ パスワードリセット\n  □ メール確認\n□ Better Auth との統合\n",[229,2634,2632],{"__ignoreMap":231},[113,2636,2638],{"id":2637},"phase-3-決済優先度-中","Phase 3: 決済（優先度: 中）",[222,2640,2643],{"className":2641,"code":2642,"language":227},[225],"□ Polar.sh アカウント作成\n  □ 組織設定\n  □ 商品/プラン作成\n□ チェックアウトフロー実装\n  □ 料金ページ作成\n  □ チェックアウトリダイレクト\n  □ 成功/キャンセルページ\n□ Webhook処理\n  □ エンドポイント作成\n  □ サブスクリプション状態管理\n□ 顧客ポータルリンク\n",[229,2644,2642],{"__ignoreMap":231},[41,2646],{},[44,2648,2650],{"id":2649},"_5-環境変数一覧","5. 環境変数一覧",[222,2652,2654],{"className":735,"code":2653,"language":356,"meta":231,"style":231},"# Better Auth\nBETTER_AUTH_SECRET=your-32-char-secret\nBETTER_AUTH_URL=https://chat-story.number55number55.workers.dev\n\n# Google OAuth（オプション）\nGOOGLE_CLIENT_ID=xxx.apps.googleusercontent.com\nGOOGLE_CLIENT_SECRET=xxx\n\n# Resend\nRESEND_API_KEY=re_xxx\n\n# Polar.sh\nPOLAR_ACCESS_TOKEN=polar_xxx\nPOLAR_WEBHOOK_SECRET=whsec_xxx\nPOLAR_ORGANIZATION_ID=org_xxx\n",[229,2655,2656,2661,2666,2671,2675,2680,2685,2689,2693,2698,2703,2707,2712,2717,2722],{"__ignoreMap":231},[244,2657,2658],{"class":246,"line":247},[244,2659,2660],{},"# Better Auth\n",[244,2662,2663],{"class":246,"line":254},[244,2664,2665],{},"BETTER_AUTH_SECRET=your-32-char-secret\n",[244,2667,2668],{"class":246,"line":289},[244,2669,2670],{},"BETTER_AUTH_URL=https://chat-story.number55number55.workers.dev\n",[244,2672,2673],{"class":246,"line":312},[244,2674,316],{"emptyLinePlaceholder":315},[244,2676,2677],{"class":246,"line":319},[244,2678,2679],{},"# Google OAuth（オプション）\n",[244,2681,2682],{"class":246,"line":341},[244,2683,2684],{},"GOOGLE_CLIENT_ID=xxx.apps.googleusercontent.com\n",[244,2686,2687],{"class":246,"line":368},[244,2688,753],{},[244,2690,2691],{"class":246,"line":377},[244,2692,316],{"emptyLinePlaceholder":315},[244,2694,2695],{"class":246,"line":391},[244,2696,2697],{},"# Resend\n",[244,2699,2700],{"class":246,"line":397},[244,2701,2702],{},"RESEND_API_KEY=re_xxx\n",[244,2704,2705],{"class":246,"line":405},[244,2706,316],{"emptyLinePlaceholder":315},[244,2708,2709],{"class":246,"line":413},[244,2710,2711],{},"# Polar.sh\n",[244,2713,2714],{"class":246,"line":430},[244,2715,2716],{},"POLAR_ACCESS_TOKEN=polar_xxx\n",[244,2718,2719],{"class":246,"line":447},[244,2720,2721],{},"POLAR_WEBHOOK_SECRET=whsec_xxx\n",[244,2723,2724],{"class":246,"line":453},[244,2725,2726],{},"POLAR_ORGANIZATION_ID=org_xxx\n",[41,2728],{},[44,2730,2732],{"id":2731},"_6-参考リソース","6. 参考リソース",[113,2734,2735],{"id":2735},"公式ドキュメント",[51,2737,2738,2748],{},[54,2739,2740],{},[57,2741,2742,2745],{},[60,2743,2744],{},"サービス",[60,2746,2747],{},"URL",[70,2749,2750,2759,2768,2777],{},[57,2751,2752,2754],{},[75,2753,80],{},[75,2755,2756],{},[22,2757,764],{"href":764,"rel":2758},[26],[57,2760,2761,2763],{},[75,2762,91],{},[75,2764,2765],{},[22,2766,1574],{"href":1574,"rel":2767},[26],[57,2769,2770,2772],{},[75,2771,102],{},[75,2773,2774],{},[22,2775,2596],{"href":2596,"rel":2776},[26],[57,2778,2779,2782],{},[75,2780,2781],{},"React Email",[75,2783,2784],{},[22,2785,2786],{"href":2786,"rel":2787},"https://react.email/docs",[26],[113,2789,2791],{"id":2790},"ボイラープレートサンプル","ボイラープレート・サンプル",[120,2793,2794,2801],{},[123,2795,2796,2797],{},"SaaSKit (TanStack Start): ",[22,2798,2799],{"href":2799,"rel":2800},"https://github.com/saaskit",[26],[123,2802,2803,2804],{},"Better Auth Examples: ",[22,2805,2806],{"href":2806,"rel":2807},"https://github.com/better-auth/better-auth/tree/main/examples",[26],[113,2809,2811],{"id":2810},"cloudflare-workers-関連","Cloudflare Workers 関連",[120,2813,2814,2821],{},[123,2815,2816,2817],{},"Hono + Cloudflare Workers: ",[22,2818,2819],{"href":2819,"rel":2820},"https://hono.dev/getting-started/cloudflare-workers",[26],[123,2822,2823,2824],{},"D1 Database: ",[22,2825,2826],{"href":2826,"rel":2827},"https://developers.cloudflare.com/d1/",[26],[41,2829],{},[44,2831,2833],{"id":2832},"_7-注意事項tips","7. 注意事項・Tips",[113,2835,2836],{"id":2836},"認証",[120,2838,2839,2842,2845],{},[123,2840,2841],{},"セッショントークンは HttpOnly Cookie で管理（XSS対策）",[123,2843,2844],{},"CSRF対策は Better Auth が自動対応",[123,2846,2847],{},"パスワードは Argon2 でハッシュ化（Better Auth標準）",[113,2849,2850],{"id":2850},"決済",[120,2852,2853,2856,2859],{},[123,2854,2855],{},"本番環境移行前にテストモードで十分にテスト",[123,2857,2858],{},"Webhookは冪等性を考慮した実装を",[123,2860,2861],{},"日本での消費税はPolar.shが自動処理",[113,2863,2864],{"id":2864},"メール",[120,2866,2867,2870,2873],{},[123,2868,2869],{},"送信ドメインは必ず認証（SPF/DKIM）",[123,2871,2872],{},"開発中はテストモードでリアル送信を防止",[123,2874,2875],{},"バウンスメールの自動処理を設定",[41,2877],{},[14,2879,2880,2884],{},[2881,2882,2883],"em",{},"このドキュメントは Chat Story サービスの実装ガイドとして作成されました。",[2881,2885,2886],{},"技術選定は2025年12月時点の情報に基づいています。",[2888,2889,2890],"style",{},"html pre.shiki code .sxvE3, html code.shiki .sxvE3{--shiki-default:#A0ADA0;--shiki-dark:#A0ADA0}html pre.shiki code .sHkkW, html code.shiki .sHkkW{--shiki-default:#1E754F;--shiki-dark:#1E754F}html pre.shiki code .shFtX, html code.shiki .shFtX{--shiki-default:#999999;--shiki-dark:#999999}html pre.shiki code .s4oTP, html code.shiki .s4oTP{--shiki-default:#B07D48;--shiki-dark:#B07D48}html pre.shiki code .sMJiu, html code.shiki .sMJiu{--shiki-default:#B5695977;--shiki-dark:#B5695977}html pre.shiki code .sdGka, html code.shiki .sdGka{--shiki-default:#B56959;--shiki-dark:#B56959}html pre.shiki code .stQ0i, html code.shiki .stQ0i{--shiki-default:#AB5959;--shiki-dark:#AB5959}html pre.shiki code .senZ8, html code.shiki .senZ8{--shiki-default:#59873A;--shiki-dark:#59873A}html pre.shiki code .sz8Xr, html code.shiki .sz8Xr{--shiki-default:#998418;--shiki-dark:#998418}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sG7-3, html code.shiki .sG7-3{--shiki-default:#393A34;--shiki-dark:#393A34}",{"title":231,"searchDepth":254,"depth":254,"links":2892},[2893,2894,2902,2909,2917,2922,2923,2928],{"id":46,"depth":254,"text":46},{"id":110,"depth":254,"text":111,"children":2895},[2896,2897,2898,2899,2900,2901],{"id":115,"depth":289,"text":115},{"id":155,"depth":289,"text":155},{"id":220,"depth":289,"text":220},{"id":234,"depth":289,"text":235},{"id":677,"depth":289,"text":677},{"id":756,"depth":289,"text":756},{"id":784,"depth":254,"text":785,"children":2903},[2904,2905,2906,2907,2908],{"id":788,"depth":289,"text":789},{"id":895,"depth":289,"text":896},{"id":967,"depth":289,"text":967},{"id":1487,"depth":289,"text":1488},{"id":1559,"depth":289,"text":756},{"id":1587,"depth":254,"text":1588,"children":2910},[2911,2912,2913,2914,2915,2916],{"id":1591,"depth":289,"text":115},{"id":1629,"depth":289,"text":949},{"id":1696,"depth":289,"text":1697},{"id":1706,"depth":289,"text":967},{"id":2537,"depth":289,"text":677},{"id":2574,"depth":289,"text":756},{"id":2602,"depth":254,"text":2603,"children":2918},[2919,2920,2921],{"id":2606,"depth":289,"text":2607},{"id":2627,"depth":289,"text":2628},{"id":2637,"depth":289,"text":2638},{"id":2649,"depth":254,"text":2650},{"id":2731,"depth":254,"text":2732,"children":2924},[2925,2926,2927],{"id":2735,"depth":289,"text":2735},{"id":2790,"depth":289,"text":2791},{"id":2810,"depth":289,"text":2811},{"id":2832,"depth":254,"text":2833,"children":2929},[2930,2931,2932],{"id":2836,"depth":289,"text":2836},{"id":2850,"depth":289,"text":2850},{"id":2864,"depth":289,"text":2864},null,"Chat Storyサービスの認証（Better Auth）・決済（Polar.sh）・メール送信（Resend）の技術選定と実装方針","md",{},"/chat-story-implementation-guide",false,"2025-12-11T00:00:00.000Z",{"title":5,"description":2934},"2025-12/2025-12-11/chat-story-implementation-guide",[2943,80,91,102,2836,2850],"Cloudflare Workers","vRON8R1QN4Xk6dbxy3S2iY9lSAJ9BDR_Uh4MkKJwXD0",[],[2947,2948,2952,2955,2959],{"title":2617,"path":2616,"publishedAt":2939},{"title":2949,"path":2950,"publishedAt":2951},"2026年7月30日の開発日記 - 3つのリポジトリに同じ問いを投げた朝","/2026-07-30-diary","2026-07-30T00:00:00.000Z",{"title":2953,"path":2954,"publishedAt":2951},"Cloudflare Worker のバンドル削減2日目、外部HTML化で287KiB空けて miller-viewer 島は予測の1割だった","/nuxt-worker-bundle-reduction-miller-viewer",{"title":2956,"path":2957,"publishedAt":2958},"2026年7月29日の開発日記 - 朝いちで ~/.claude が消えた日に、7本の作業が並走した","/2026-07-29-diary","2026-07-29T00:00:00.000Z",{"title":2960,"path":2961,"publishedAt":2958},"Vueページは必ずWorkerバンドルに載る — 静的HTML化と「島マウント」でCloudflare Workersの上限から逃げる","/worker-bundle-reduction-content-islands","https://log.eurekapu.com/og/blog/chat-story-implementation-guide.png?v=2025-12-11T00%3A00%3A00.000Z&title=Chat%20Story%20%E5%AE%9F%E8%A3%85%E3%82%AC%E3%82%A4%E3%83%89&author=Kei%20Komatsu&sig=f562e02687baa80b",1785654675115]