Web_Advance
  • 本書簡介
  • Node.js 部分
    • Node 版本管理
    • 使用NPM
      • Yarn
    • 開始Node
    • Worker Thread
    • REPL
    • TCP
    • path
    • Cluster and Child_process
    • assert (自訂拋出的錯誤)
    • Stream(流)
    • util (工具類)
    • EventEmitter
    • fs 文件操作
    • Buffer
      • Binary Diff
      • 查看 Binary 檔案內容
    • Process (進程)
    • 錯誤處理
  • OS
  • Async Hook
  • TCP
  • HTTP
    • 有關爬蟲
    • HTTP/2
    • HTTP Protocal
  • HTTPS
    • HTTPS 流程
    • SSL pinning
    • HTTPS 封包解密
    • 建立自簽發 HTTPS 證書
    • 幫網站加上HTTPS
    • HTTPS原理
  • Crypto加密
  • 有關繼承
  • JS 基本
    • JavaScript 迴圈與異步處理
  • 使用 Express
    • 上傳檔案
    • 圖片伺服器
    • 簡單範例
  • 使用 Nest.js
  • 使用MongoDB
    • 設置帳戶登入權限
    • Mongoose 框架
    • 進階Mongo
    • 基本環境操作
    • MongoDB Sharding
  • 使用MySQL
    • Schema 架構設計
    • SQL 語法
    • SQL Procedure
    • Node.js 操作 MySQL
    • 使用 Sequelize
      • DB Migration
      • function
  • 使用PostgreSQL
    • 常見問題
    • replica
    • 基本指令
    • 使用Node.js操控pg
    • SQL 基礎
  • 使用TypeORM
  • RethinkDB
  • CSS 深度探討
    • Width, Height
  • React
    • 第三方組件
      • Formik
    • styled component
    • propTypes
    • React webpack 部署
    • React util
    • 寫component並且publish
    • create-react-app
    • Context API
    • i18n
    • Server side render
    • Next.js教學
    • Higher order component 與 Recompose
    • component 間 互相存取
    • React hook
  • React router
    • 自己寫一個Router
  • Redux
    • Redux Toolkit
    • 小技巧
    • Redux sagas
    • compose
  • React Native
    • adb
    • InApp Billing
    • Icon
    • SVG
    • Firebase
      • Phone Auth
    • 自動化測試
    • Splash screen
    • Websocket
    • Googla OAuth
      • iOS
      • Android
    • Facebook OAuth
      • iOS
      • Android
    • IOS
    • 第三方組件
      • Auth Code Input
      • Country Code Picker
      • onboarding screen
      • Toast
    • ESlint
    • Push notification
    • Android 上架步驟
    • Expo
    • router
      • react-navigation套件
    • 原生組件
      • RefreshControl
      • Modal
      • Alert
      • button
      • KeyboardAvoidingView
      • Drawer
      • Image
    • 限制螢幕垂直與水平
    • NativeBase UI
    • Debug
    • 常見問題
    • Network
    • 硬體操作
      • 隱藏鍵盤
      • 地理位置
      • 相機與圖片庫存取
    • Async Storage
    • Animation
    • Admob
  • JS 模組化
  • 使用 Webpack
  • 使用 Babel
  • JWT Token
  • ES6 ES7 ES8
    • Array method
    • ES8 Async
    • ES6 Proxy
    • ES6 Object
    • ES6 Arrow function
    • ES6 Promise
    • ES6 Symbol
    • ES6 Generator
    • ES6 Set,Map
    • ES6 Class
  • 模板引擎
    • Mustache
    • Handlebars.js
    • EJS
  • ESLint
  • 部屬到OpenShift
  • OpenStack
  • OAuth
    • Twitter OAuth
    • Google authenticator
    • facebook oauth
      • facebook like ,share
    • google oauth
  • Redis
  • 做一個簡單的markdown editor
  • Websocket
    • WebSocket 相關 Protocol
  • Sublime 安裝套件
  • Google api
    • Cloud Run
    • speech api
    • place autocomplete
    • Geocode
    • Map
      • React map
    • vision api
    • Google-recaptcha
    • Google sheet
  • Instagram API
  • Markdown 與 code pretty js
  • HTML5
    • IntersectionObserver
    • HTML5 audio
    • HTML5 Video 與 WebRTC
      • WebRTC 進階
      • WebEX API
    • HTML5 IndexedDB
  • Google Cloud Platform (GCP)
    • Cloud Storage
    • Cloud storage 串接 Cloud CDN
  • Vim 編輯器
  • 使用nginx
    • config
  • Unix 實用指令
    • 新 VPS 安裝流程
    • Ubuntu 22 安裝
    • Shell Script 教學
  • Git 實用指令
    • Git hook
    • 加上 SSH-key 到 GitHub
    • GPG簽名
  • SSH 實用指令
  • 有關Fetch與axios與跨域請求
  • 圖片上傳相關
    • imgur API
  • JS 格式轉換
  • js trick
  • AWS
    • AWS EBS
    • AWS HTTPS 憑證
    • AWS Cloudfront、ELB、ACL
    • AWS Athena
    • AWS CloudWatch、SNS
    • AWS RDS
    • AWS lambda
      • 範例
      • 加上權限控管
    • AWS S3
    • AWS DynamoDB
      • 結合Lambda
    • 快速把 EC2 串上 AWS Cloudfront CDN
    • AWS 證照相關
  • 有關日期Date
  • VS code 編輯器
    • VSCode 外掛 Plugin
  • CI with Gitlab&Jenkins
  • API 測試
    • Postman
      • 設置 Postman 環境變數
    • API Blueprint
    • swagger
      • 註解寫在Code內生成swagger UI
  • Javascript 實用Lib
  • 遠端寫程式
  • Quicktime錄影注意事項
  • Web開發進階Bug
  • Web壓力測試
  • LineBot
  • PM2部署
  • i18n
  • VPN
  • Protocol Buffers
  • Docker教學
    • LXC LXD
    • Docker Compose
    • Docker 原理
    • Docker 指令
  • E2E Testing
    • Cypress
    • PlayWright
    • Puppeteer 與其他 UI 測試工具
  • Unit Test (Jest & enzyme)
    • React Testing Library
    • mocha
  • Cassandra
    • cluster
  • Distribute Web
    • Dat project
    • IPFS project
  • Cluster and Child_process
  • 打包應用程式
  • Java
    • 使用gradle結合docker
  • Debug 頁面
  • Proxy
  • Chrome extension
  • 消息系統
    • RabbitMQ
  • 金流串接
    • Paypal
    • spgateway智富通
    • Stripe 串接
  • 有關Log
  • 設定 feature flag
  • Azure
    • Face API
    • Image Analyze API
    • Azure Serverless
    • Cosmos DB
      • 使用 SDK
      • 以 RESTful 操作 DB
      • 一致性策略與 DB replicate
  • NodeBB 筆記
  • 瀏覽器快取與緩存(Etag, If-None-Match)
  • 瀏覽器快取與緩存(Expires, Last-modified, Cache-Control)
  • Node.js 第三方模組
    • OpenCV
  • Kubernetes
    • 本地測試 MiniKube
  • Ngrok 使用
  • Telegram MiniAPP 開發
  • Firebase 教學
  • 演算法筆記
  • 圖表
    • Echart
    • TradingView 圖表
    • D3
    • 熱力圖 heatmap
  • 後端緩存 Cache
  • 資料一致性
  • Web 安全機制
    • Cookie 與 LocalStorage
  • Vue
    • Element UI
    • Devtool
    • Vuex
    • Vue router
  • 相關網路協議
    • 網路 IP 基礎
    • Google Search 技巧
    • 網路診斷工具
    • IP
    • DNS
  • GitLab 與 Drone
  • SMTP、POP、IMAP
    • SendGrid
  • IPC
  • 串流服務
    • Twilio
    • Agora
  • 其他資源
  • GraphQL
  • Typescript
  • UI 相關資源
  • FFmpeg
  • Unity 遊戲開發筆記
  • Influx DB
  • Windows 相關
  • DALL·E 3
  • Coap
  • Slack API
  • 資訊安全
    • 破解 ZIP 密碼
Powered by GitBook
On this page
  • Stripe
  • 原理:
  • 使用React
  • 確認付款
  • 客製化按鈕
  • 前端完整版範例:
  • 訂閱機制
  • 存入卡片資料供後續使用

Was this helpful?

Edit on GitHub
  1. 金流串接

Stripe 串接

Previousspgateway智富通Next有關Log

Last updated 5 years ago

Was this helpful?

Stripe

stripe為一個金流串接服務提供者,其特點在於串接簡單。

API文件:

1.先到其網站註冊帳號:

需要具有其許可國家內的銀行帳號與手機。

2.接著就是試著串接API

  • 程式範例:

  • 商店Dashboard:

建立商店後會可以進入一個Dashboard介面,在左下方把Test打開即可進入測試模式。

3.程式範例:

Server.js

const keyPublishable = process.env.PUBLISHABLE_KEY;
const keySecret = process.env.SECRET_KEY;

const app = require("express")();
const stripe = require("stripe")(keySecret);

app.set("view engine", "pug");
app.use(require("body-parser").urlencoded({extended: false}));

app.get("/", (req, res) =>
  res.render("index.pug", {keyPublishable}));

app.post("/charge", (req, res) => {
  let amount = 500;

  stripe.customers.create({
     email: req.body.stripeEmail,
    source: req.body.stripeToken
  })
  .then(customer => {
    stripe.charges.create({
      amount,
      description: "Sample Charge",
         currency: "usd",
         customer: customer.id
    })
    console.log(req.body.stripeToken)
    console.log(req.body.stripeToken)
  }
  )
  .then(charge => res.render("charge.pug"));
});

app.listen(4567);

views/index.pug

<html>
<head>
<script src="https://checkout.stripe.com/v2/checkout.js"></script></head>

<body>
    <form action="/charge" method="post">
        <article><label>Amount: $5.00</label></article>
        <script class="stripe-button" data-key="pk_test_nk4UQNZM8NWjfvmhKjfZnWav" src="//checkout.stripe.com/v2/checkout.js" data-locale="auto" data-description="Sample Charge" data-amount="500"></script>
    </form>
</body>

</html>

views/charge.pug

h2 You successfully paid <strong>$5.00</strong>!

原理:

1.引入stripe的官方script後,加入一個元件

    <form action="/charge" method="post">
        <article><label>Amount: $5.00</label></article>
        <script class="stripe-button" data-key="pk_test_nk4UQNZM8NWjfvmhKjfZnWav" src="//checkout.stripe.com/v2/checkout.js" data-locale="auto" data-description="Sample Charge" data-amount="500"></script>
    </form>

此元件會產生官方的付款按鈕

2.之後點擊按鈕後會要求填入信用卡資訊與email

3.送出後會發送Request給stripe

然後stripe成功後回傳request給剛才元件上寫的Endpoint,也就是form上寫的action位置

4.接著我們後端App.js接到後會執行

  stripe.customers.create({
     email: req.body.stripeEmail,
    source: req.body.stripeToken
  })

產生如下請求

    stripe.charges.create({
      amount,
      description: "Sample Charge",
         currency: "usd",
         customer: customer.id
    })

以上的請求詳細內容都可以在左側log tab看到

使用React

可使用此模組:

測試用信用卡:4242 4242 4242 4242

測試用的key也不同

流程:

1.使用官方的元件輸入信用卡號碼與資訊,然後前端呼叫stripe.createPaymentMethod 傳給後端

2.後端呼叫並產生stripe.paymentIntents 然後將paymentIntent.client_secret 傳給前端

3.前端使用stripe.confirmCardPayment 加上剛才的paymentIntent.client_secret確認交易

4.成功後再發一個request給後端更新使用者購買狀態

yarn add @stripe/react-stripe-js @stripe/stripe-js
import React from "react";
import { loadStripe } from "@stripe/stripe-js";
import axios from "axios";
import {
  CardElement,
  Elements,
  ElementsConsumer
} from "@stripe/react-stripe-js";

class CheckoutForm extends React.Component {
  handleSubmit = async event => {
    const billing_details = {
      email: "Jenny@gmail.com",
      name: "Jenny Rosen"
    };
    event.preventDefault();
    const { stripe, elements } = this.props;
    const { error, paymentMethod } = await stripe.createPaymentMethod({
      type: "card",
      card: elements.getElement(CardElement),
      billing_details,
    });
    if (!error) {
      axios
        .post(`http://localhost:8081/stripepay`, paymentMethod)
        .then(async response => {
          const { client_secret } = response.data;
          if (client_secret) {
            const result = await stripe.confirmCardPayment(client_secret, {
              payment_method: {
                card: elements.getElement(CardElement),
                billing_details,
              }
            });
            if (result.paymentIntent.status === "succeeded") {
              window.alert("成功", "", "success");
              //TODO send another request to update backend customer status
            } else {
              window.alert("付款失敗");
            }
          }
        })
        .catch(err => {
          console.log(err);
        });
    } else {
      console.log(error);
    }
  };

  render() {
    const { stripe } = this.props;
    return (
      <form onSubmit={this.handleSubmit}>
        <CardElement />
        <button type="submit" disabled={!stripe}>
          Pay
        </button>
      </form>
    );
  }
}

const InjectedCheckoutForm = () => (
  <ElementsConsumer>
    {({ stripe, elements }) => (
      <CheckoutForm stripe={stripe} elements={elements} />
    )}
  </ElementsConsumer>
);

const stripePromise = loadStripe("pk_test_....");

const App = () => (
  <Elements stripe={stripePromise}>
    <InjectedCheckoutForm />
  </Elements>
);

export default App;

Server.js

const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const cors = require('cors')

// create application/json parser
app.use(bodyParser.json());
// create application/x-www-form-urlencoded parser
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cors())

app.post("/stripepay", async (req, res) => {
  console.log(req.body)
  const stripe = require("stripe")("sk_test_....");
  let amount = 1700;
  const paymentIntent = await stripe.paymentIntents.create({
    amount,
    description: 'Im description',
    currency: 'twd',
    payment_method_types: ['card'],
    metadata: {
      order_id: 6735,
    },
  });
  res.end(JSON.stringify({
    client_secret: paymentIntent.client_secret,
  }))
});

app.listen(8081, () => console.log('app start'));

確認付款

使用stripe.paymentIntents.create 回傳的client secret 並丟給前端,然後前端使用stripe.confirmCardPayment(clientSecret)

來判斷

客製化按鈕

可直接加上css

input, 
.StripeElement {
  display: block;
  margin: 10px 0 20px 0;
}

.StripeElement.PaymentRequestButton {
  height: 40px;
}

前端完整版範例:

App.js

import React, { useMemo } from "react";
import {
  useStripe,
  useElements,
  ElementsConsumer,
  Elements,
  CardNumberElement,
  CardCvcElement,
  CardExpiryElement
} from "@stripe/react-stripe-js";
import { loadStripe } from "@stripe/stripe-js";
import "./App.css";
import axios from "axios";

const useOptions = () => {
  const options = useMemo(() => ({
    style: {
      base: {
        color: "#424770",
        letterSpacing: "0.025em",
        fontFamily: "Source Code Pro, monospace",
        "::placeholder": {
          color: "#aab7c4"
        }
      },
      invalid: {
        color: "#9e2146"
      }
    }
  }));

  return options;
};

const SplitForm = () => {
  const stripe = useStripe();
  const elements = useElements();
  const options = useOptions();

  const handleSubmit = async event => {
    const billing_details = {
      email: "Jenny@gmail.com",
      name: "Jenny Rosen"
    };
    event.preventDefault();
    const { error, paymentMethod } = await stripe.createPaymentMethod({
      type: "card",
      card: elements.getElement(CardNumberElement),
      billing_details
    });
    if (!error) {
      axios
        .post(`http://localhost:8081/stripepay`, paymentMethod)
        .then(async response => {
          const { client_secret } = response.data;
          if (client_secret) {
            const result = await stripe.confirmCardPayment(client_secret, {
              payment_method: {
                card: elements.getElement(CardNumberElement),
                billing_details
              }
            });
            if (result.paymentIntent.status === "succeeded") {
              window.alert("成功", "", "success");
              //TODO send another request to update backend customer status
            } else {
              window.alert("付款失敗");
            }
          }
        })
        .catch(err => {
          console.log(err);
        });
    } else {
      console.log(error);
    }
  };
  return (
    <form className="StripeForm" onSubmit={handleSubmit}>
      <label>
        Card number
        <CardNumberElement
          options={options}
          onReady={() => {
            console.log("CardNumberElement [ready]");
          }}
          onChange={event => {
            console.log("CardNumberElement [change]", event);
          }}
          onBlur={() => {
            console.log("CardNumberElement [blur]");
          }}
          onFocus={() => {
            console.log("CardNumberElement [focus]");
          }}
        />
      </label>
      <label>
        Expiration date
        <CardExpiryElement
          options={options}
          onReady={() => {
            console.log("CardNumberElement [ready]");
          }}
          onChange={event => {
            console.log("CardNumberElement [change]", event);
          }}
          onBlur={() => {
            console.log("CardNumberElement [blur]");
          }}
          onFocus={() => {
            console.log("CardNumberElement [focus]");
          }}
        />
      </label>
      <label>
        CVC
        <CardCvcElement
          options={options}
          onReady={() => {
            console.log("CardNumberElement [ready]");
          }}
          onChange={event => {
            console.log(event);
            console.log("CardNumberElement [change]", event);
          }}
          onBlur={() => {
            console.log("CardNumberElement [blur]");
          }}
          onFocus={() => {
            console.log("CardNumberElement [focus]");
          }}
        />
      </label>
      <button type="submit" disabled={!stripe}>
        Pay
      </button>
    </form>
  );
};

const InjectedCheckoutForm = () => (
  <ElementsConsumer>
    {({ stripe, elements }) => (
      <SplitForm stripe={stripe} elements={elements} />
    )}
  </ElementsConsumer>
);

const stripePromise = loadStripe("pk_test_");

const App = () => (
  <Elements stripe={stripePromise}>
    <InjectedCheckoutForm />
  </Elements>
);

export default App;

App.css

* {
  box-sizing: border-box;
}

body,
html {
  background-color: #f6f9fc;
  font-size: 18px;
  font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
  margin: 0;
}

.DemoPickerWrapper {
  padding: 0 12px;
  font-family: "Source Code Pro", monospace;
  box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
  border-radius: 3px;
  background: white;
  margin: 24px 0 48px;
  width: 100%;
}

label {
  color: #6b7c93;
  font-weight: 300;
  letter-spacing: 0.025em;
}

input,
.StripeElement {
  display: block;
  margin: 10px 0 20px 0;
  max-width: 500px;
  padding: 10px 14px;
  font-size: 1em;
  font-family: "Source Code Pro", monospace;
  box-shadow: rgba(50, 50, 93, 0.14902) 0px 1px 3px,
    rgba(0, 0, 0, 0.0196078) 0px 1px 0px;
  border: 0;
  outline: 0;
  border-radius: 4px;
  background: white;
}

input::placeholder {
  color: #aab7c4;
}

input:focus,
.StripeElement--focus {
  box-shadow: rgba(50, 50, 93, 0.109804) 0px 4px 6px,
    rgba(0, 0, 0, 0.0784314) 0px 1px 3px;
  -webkit-transition: all 150ms ease;
  transition: all 150ms ease;
}

.StripeElement.IdealBankElement,
.StripeElement.FpxBankElement,
.StripeElement.PaymentRequestButton {
  padding: 0;
}

.StripeElement.PaymentRequestButton {
  height: 40px;
}

.StripeForm button {
  white-space: nowrap;
  border: 0;
  outline: 0;
  display: inline-block;
  height: 40px;
  line-height: 40px;
  padding: 0 14px;
  box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
  color: #fff;
  border-radius: 4px;
  font-size: 15px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.025em;
  background-color: #6772e5;
  text-decoration: none;
  -webkit-transition: all 150ms ease;
  transition: all 150ms ease;
  margin-top: 10px;
}

.StripeForm button:hover {
  color: #fff;
  cursor: pointer;
  background-color: #7795f8;
  transform: translateY(-1px);
  box-shadow: 0 7px 14px rgba(50, 50, 93, 0.1), 0 3px 6px rgba(0, 0, 0, 0.08);
}

訂閱機制

存入卡片資料供後續使用

1.在前端使用createPaymentMethod 並發請求給後端之後在後端使用

server.js

  const customer = await stripe.customers.create({
    name: 'Jenny',
    email: 'Jenny@gmail.com'
  });
// 記得把 customer.id 存入 DB 

// 並且在paymentIntent加入custom id 欄位

  const paymentIntent = await stripe.paymentIntents.create({
    // .....,
    customer: customer.id,
  });

web.js

// 新增 setup_future_usage 欄位在confirmCardPayment
const result = await stripe.confirmCardPayment(client_secret, {
  // .....,
 setup_future_usage: 'off_session'
});

之後客戶成功刷卡後的付款資訊就會存入stripe

2. 讀取先前存入的付款資訊

  const customerId = // 從DB讀取先前 stripe.customers.create 並付款的使用者ID
  const paymentMethods = await stripe.paymentMethods.list({
    customer: customerId,
    type: 'card',
  });
  const paymentIntentSaved = await stripe.paymentIntents.create({
    amount: 1200,
    currency: 'sgd',
    customer: customerId,
    payment_method: paymentMethods.data[0].id,
    off_session: true,
    confirm: true,
  });
  // sending paymentIntentSaved's client_secret to frontend

最後把 paymentIntentSaved 中的 client_secret 給前端然後 confirmPayment 即可

要求客戶付款的都在checkout文件:

5.最後前一個請求返回後會進行確認請求

文件:

https://stripe.com/docs/checkout
https://github.com/stripe/react-stripe-js
https://stripe.com/docs/stripe-js/react
https://stripe.com/docs/payments/payment-intents/verifying-status
https://github.com/stripe/elements-examples
https://stripe.com/docs/stripe-js
https://stripe.com/docs/payments/save-during-payment
https://stripe.com/docs/api
https://stripe.com
https://stripe.com/docs/checkout/express
https://dashboard.stripe.com
Build a subscription
Logo