ECサイトで決済システムStripeを使いたい

<h2>Node.jsとStripeを使った決済システムの作り方</h2>

ユーザー側のWEBサイトの構築(index.html等)
　→　サーバーサイドをnode.js 
　→　決済システムはStripeを使用


Node.jsの知識が少し必要かもしれないがそこはスルーします

---
好きなファイル名でフォルダを作成
フォルダ名はECサイトで決済システムStripeを使いたい（英語推奨）


ターミナルを開いてpackage.jsonを構築
```php:terminal
npm init -y
```

【index.js】の名前を今回は【server.js】に変えて作業をしようと思うので【package.json】を開き"main":server.jsとscript　"dev": "nodemon server.js"の記述を追記する
```php:package.json
{
  {
  "description": "",
  "main":"server.js",//ここを追記
  "scripts": {
    "dev": "nodemon server.js"
  },//この部分も追記
  "dependencies": {
    "express": "^4.18.2",
    "nodemon": "^2.0.22",
    "stripe": "^12.11.0"
  } 
}
```
ターミナルを実行しインストール

```php:terminal
// express nodemon stripeのインストール
npm install -i express nodemon stripe 
```

expressは最小限のWebフレームワーク

nodemonはソースを監視し自動でサーバーを再起動してくれるツール。
※nodeの代わりにnodemonを使ってコードを走らせると、コードの変更時にプロセスが自動で再起動する。

---

server.jsを上で作ったフォルダ（ECサイトで……）の直下に作成していく
```php:ECサイトで……\server.js
const express =require("express");
const app = express();
const PORT = 3000

app.listen(PORT,console.log("サーバーが起動しました"));
```
```
npm run dev
```

サーバーが立ち上がる。

※localhost:3000をブラウザで開いてもまだCannot GetとなりタブがErrorと表示されている。

<h2>Stlipeを検索</h2>

https://stripe.com/jp

サインインして設定をしていく。
※Webサイトの設定はインスタで代用。

サインイン後ホーム本番環境かテスト環境がタブ右側で選べるので今回はテスト環境を使用

右側中央にある公開可能キー、secret keyの下部に、ドキュメントがあるので使用方法はここを見る。

サブスクリプションのクイックスタートを選択。
タブでフロントエンド、バックエンドの参照が選べるので今回はRUBYではなくNODEを選択

<h2>UI（ユーザー側のWEBサイト）を作成していく</h2>
localhost:3000のCannot GetのError表示をなくしていく

まずserver.jsにexpressを採用する

```php:server.js
app.use(express.static("public"));
```



publicフォルダを作成（node_modulesと同じ階層）その中にindex.htmlを作成していく
※formタグでaction="/create-checkout-session" method="POST"を指定することで申し込みを押すとPOSTするような仕組みになる


```php:public/index.html
<!DOCTYPE html>
<html>
  <head>
    <title>Subscribe to a cool new product</title>
    <link rel="stylesheet" href="style.css" />
    <script src="https://polyfill.io/v3/polyfill.min.js?version=3.52.1&features=fetch"></script>
    <script src="https://js.stripe.com/v3/"></script>
  </head>
  <body>
    <section>
      <div class="product">
        <div class="description">
          <h3>スタータープラン</h3>
          <h5>2000円 / 月</h5>
        </div>
      </div>
      <form action="/create-checkout-session" method="POST">
        <!-- Add a hidden field with the lookup_key of your Price -->
        <input type="hidden" name="lookup_key" value="{{PRICE_LOOKUP_KEY}}" />
        <button id="checkout-and-portal-button" type="submit">
          申し込みはこちら
        </button>
      </form>
    </section>
  </body>
</html>
```
一応CSSをあてる
```
body {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    background: #242d60;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto',
      'Helvetica Neue', 'Ubuntu', sans-serif;
    height: 100vh;
    margin: 0;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
  }
  section {
    background: #ffffff;
    display: flex;
    flex-direction: column;
    width: 400px;
    height: 112px;
    border-radius: 6px;
    justify-content: space-between;
    margin: 10px;
  }
  .product {
    display: flex;
    justify-content: center;
    width: 100%;
    height: 100%;
    align-items: center;
  }

  p {
    font-style: normal;
    font-weight: 500;
    font-size: 14px;
    line-height: 20px;
    letter-spacing: -0.154px;
    color: #242d60;
    height: 100%;
    width: 100%;
    padding: 0 20px;
    display: flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
  }
  svg {
    border-radius: 6px;
    margin: 10px;
    width: 54px;
    height: 57px;
  }
  h3,
  h5 {
    font-style: normal;
    font-weight: 500;
    font-size: 14px;
    line-height: 20px;
    letter-spacing: -0.154px;
    color: #242d60;
    margin: 0;
  }
  h5 {
    opacity: 0.5;
  }
  a {
    text-decoration: none;
    color: white;
  }
  #checkout-and-portal-button {
    height: 36px;
    background: #556cd6;
    color: white;
    width: 100%;
    font-size: 14px;
    border: 0;
    font-weight: 500;
    cursor: pointer;
    letter-spacing: 0.6;
    border-radius: 0 0 6px 6px;
    transition: all 0.2s ease;
    box-shadow: 0px 4px 5.5px 0px rgba(0, 0, 0, 0.07);
  }
  #checkout-and-portal-button:hover {
    opacity: 0.8;
  }

```

localhost:3000をURLに記載すると立ち上がる。

筆者はスペルミスでエラー表記がでたので参考までに
"express"を"expresss"にしていた為下記エラーが発生
```
ReferenceError: expresss is not defined
    at Object.<anonymous> (C:\Users\kohsu\デスクトップ\Homepage\Node\ECサ
イトで決済システムStripeを使いたい\server.js:5:9)
    at Module._compile (node:internal/modules/cjs/loader:1256:14)        
    at Module._extensions..js (node:internal/modules/cjs/loader:1310:10) 
    at Module.load (node:internal/modules/cjs/loader:1119:32)
    at Module._load (node:internal/modules/cjs/loader:960:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:23:47

Node.js v18.16.1
[nodemon] app crashed - waiting for file changes before starting...  
```




作ったスタータープランはまだstripeで用意していないのでwebuiの管理画面で作っていく



<h2>Stripeでデータ作成</h2>

タブで【商品】に進み、右側にある【商品を追加】を押す

情報を入れて商品を【登録】ボタンを押す。ダッシュボードに追加するためログインパスワードが聞かれるので入力。商品プランができあがる。

<h2>node.jsでユーザーから受け取った情報をStripeに投げたい</h2>

index.htmlに/create-checkout-sessionをPOSTメソッドで設定してあるので
server.jsで受け取った時の処理を書き足す

公式を参考に書いていく

https://stripe.com/docs/billing/quickstart

Stripeと連携するためにはsecretキーが必要なので公式ドキュメントを参考にserver.jsに
```php:server.js
const stripe = require('stripe')('sk_test_51NPcUkFDuVd07gltHfx5iQ3OekXzgb4ZkIeOUi0jvmmRNg98r5yZTkUY0DSstcRSikf5Qzjf3yI1b6bXJMosLp6e00Zx8zf74J');
```
を記述する。SecretKeyは自分のものを使用する。ホームの右中央に記載がある。
※ホーム　→　ドキュメント　→　サブスクリプションの作成　→　エンドポイントを追加する
に記載があるものは自分のSecretKeyが適用されている。
本番環境のSecretKeyはもちろん他人に見せちゃダメ！！


<h2>中身を書いていく</h2>

```
const express =require("express");
const app = express();
const PORT = 3000
const stripe = require('stripe')('sk_test_51NPcUkFDuVd07gltHfx5iQ3OekXzgb4ZkIeOUi0jvmmRNg98r5yZTkUY0DSstcRSikf5Qzjf3yI1b6bXJMosLp6e00Zx8zf74J');

app.use(express.static("public"));


app.post("/create-checkout-session", async (req, res) => {
  try {
    const prices = await stripe.prices.list({});
    console.log(prices);
  } catch(err){
    console.log(err);
  }
});

```
npm run devを実行しlocalhost:3000の【申し込みはこちら】を押すとコンソールに
Stripeの情報が出力される


---
補足
app.post("/create-checkout-session", async (req, res)
は"/create-checkout-session"がPOSTされたら（ボタンが押されたら）非同期処理で実行という意味


const prices = await stripe.prices.list({});
非同期処理なのでawaitで少し待つ



<h2>session（ユーザーの状態）を追加していく</h2>
const session =では上記コンソールで取得した情報（Stripeで作った情報）をとってきている

price:price.data[0].id,はIDを
quantity:1でサブスクの回数を指定している。

res.redirect(303, session.url);
契約が終わったらリダイレクトする



```
const express =require("express");
const app = express();
const PORT = 3000
const stripe = require('stripe')('sk_test_51NPcUkFDuVd07gltHfx5iQ3OekXzgb4ZkIeOUi0jvmmRNg98r5yZTkUY0DSstcRSikf5Qzjf3yI1b6bXJMosLp6e00Zx8zf74J');

const YOUR_DOMAIN ="http://localhost:3000"

app.use(express.static("public"));



app.post("/create-checkout-session", async (req, res) => {
  try {
    const prices = await stripe.prices.list({});
    //console.log(prices);
    const session = await stripe.checkout.sessions.create({
        line_items:[
            {
                price:prices.data[0].id,
                quantity:1
            },
        ],
        mode: "subscription",
      success_url: `${YOUR_DOMAIN}/success.html?session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: `${YOUR_DOMAIN}/cancel.html`,
    });

    res.redirect(303, session.url);
    
  } catch(err){
    console.log(err);
  }
});

```
成功したらどこに飛ばすのか指定している
```
const YOUR_DOMAIN ="http://localhost:3000"

success_url: `${YOUR_DOMAIN}/success.html?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${YOUR_DOMAIN}/cancel.html`,
```

筆者はここで申し込みはこちらですを押してエラーが出たので参考までに
price:prices.data[0].id,がprice:price.data[0].idになっていた。スペルミス
```php:terminal
ReferenceError: price is not defined
    at C:\Users\kohsu\デスクトップ\Homepage\Node\ECサイトで決済システムStripeを使いたい\server.js:15:23
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
```

またまたエラーが出たので参考までに
const YOUR_DOMAIN =　h★ttp://localhost:3000を
h★ttp://をつけづにlocalhost:3000だけで記載していた。
```
StripeInvalidRequestError: Not a valid URL
    at StripeError.generate (C:\Users\kohsu\デスクトップ\Homepage\Node\ECサイトで決済システムStripeを使いたい\node_modules\stripe\cjs\Error.js:10:20)
    at res.toJSON.then.Error_js_1.StripeAPIError.message (C:\Users\kohsu\デスクトップ\Homepage\Node\ECサイトで決済システムStripeを使いたい\node_modules\stripe\cjs\RequestSender.js:105:54)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
```

<h2>success.htmlとcancel.htmlの作成</h2>
success_url: `${YOUR_DOMAIN}/success.html?session_id={CHECKOUT_SESSION_ID}`,

cancel_url: `${YOUR_DOMAIN}/cancel.html`,の行先をつくってあげる

```php:public/success.html
<!DOCTYPE html>
<html>
  <head>
    <title>Thanks for your order!</title>
    <link rel="stylesheet" href="style.css" />
    <script src="client.js" defer></script>
  </head>
  <body>
    <section>
      <div class="product Box-root">
        <div class="description Box-root">
          <h3>スタータープランに加入しました！</h3>
        </div>
      </div>
      <form action="/create-portal-session" method="POST">
        <input type="hidden" id="session-id" name="session_id" value="" />
        <button id="checkout-and-portal-button" type="submit">
          領収書を確認する
        </button>
      </form>
    </section>
  </body>
</html>
```

```php:public/cancel.html
<!DOCTYPE html>
<html>
  <head>
    <title>Checkout canceled</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <section>
      <p>Picked the wrong subscription? Shop around then come back to pay!</p>
    </section>
  </body>
</html>

```

<h2>ようやく申し込みはこちらを押せる</h2>
テストで使用する場合カード情報などは4242 4242 4242 4242で
有効な将来の日付 (12/34 など)。
任意の 3 桁(123) (American Express カードの場合は 4 桁) のセキュリティーコードを使用します。
名前も任意で支払いを押すと領収書を押す（success_url=success.html）に飛ぶ



<h2>他のエラーを記載</h2>

Stripeのページで左上に新規ビジネスと書かれたところに名前を入れると繋がるらしい
```
StripeInvalidRequestError:IN order to use Checkout, you must set an account or business name at https://dashboard.stripe.com/account.)
```

---
領収書などほかの詳細を追加したいならば、ドキュメントを参照に作りこみをしていく

アクセス権などは大事。トライアル期間（7日間に設定）を追加なども記載がある

参考サイト：プログラミングチュートリアル
https://www.youtube.com/watch?v=gGkqFefpkVI