[譯] 使用 Stripe, Vue.js 和 Flask 接受付款

Leben_Ito發表於2019-01-14

在本教程中,我們將會開發一個使用 Stripe(處理付款訂單),Vue.js(客戶端應用)以及 Flask(服務端 API)的 web 應用來售賣書籍。

這是一個進階教程。我們預設您已經基本掌握了 Vue.js 和 Flask。如果你還沒有了解過它們,請檢視下面的連結以瞭解更多:

  1. Introduction to Vue
  2. Flaskr: Intro to Flask, Test-Driven Development (TDD), and JavaScript
  3. 用 Flask 和 Vue.js 開發一個單頁面應用

最終效果

final app

主要依賴

  • Vue v2.5.2
  • Vue CLI v2.9.3
  • Node v10.3.0
  • NPM v6.1.0
  • Flask v1.0.2
  • Python v3.6.5

目錄

目的

在本教程結束的時候,你能夠...

  1. 獲得一個現有的 CRUD 應用,由 Vue 和 Flask 驅動
  2. 建立一個訂單結算元件
  3. 使用原生 JavaScript 驗證一個表單
  4. 使用 Stripe 驗證信用卡資訊
  5. 通過 Stripe API 處理付款

專案安裝

Clone flask-vue-crud 倉庫,然後在 master 分支找到 v1 標籤:

$ git clone https://github.com/testdrivenio/flask-vue-crud --branch v1 --single-branch
$ cd flask-vue-crud
$ git checkout tags/v1 -b master
複製程式碼

搭建並啟用一個虛擬環境,然後執行 Flask 應用:

$ cd server
$ python3.6 -m venv env
$ source env/bin/activate
(env)$ pip install -r requirements.txt
(env)$ python app.py
複製程式碼

上述搭建環境的命令可能因作業系統和執行環境而異。

用瀏覽器訪問 http://localhost:5000/ping。你會看到:

"pong!"
複製程式碼

然後,安裝依賴並在另一個終端中執行 Vue 應用:

$ cd client
$ npm install
$ npm run dev
複製程式碼

轉到 http://localhost:8080。確保 CRUD 基本功能正常工作:

v1 app

想學習如何構建這個專案?檢視 用 Flask 和 Vue.js 開發一個單頁面應用 文章。

我們要做什麼?

我們的目標是構建一個允許終端使用者購買書籍的 web 應用。

客戶端 Vue 應用將會顯示出可供購買的書籍並記錄付款資訊,然後從 Stripe 獲得 token,最後傳送 token 和付款資訊到服務端。

然後 Flask 應用獲取到這些資訊,並把它們都打包傳送到 Stripe 去處理。

最後,我們會用到一個客戶端 Stripe 庫 Stripe.js,它會生成一個專有 token 來建立賬單,然後使用服務端 Python Stripe 庫和 Stripe API 互動。

final app

和之前的 教程 一樣,我們會簡化步驟,你應該自己處理產生的其他問題,這樣也會加強你的理解。

CRUD 書籍

首先,讓我們將購買價格新增到伺服器端的現有書籍列表中,然後在客戶端上更新相應的 CRUD 函式 GET,POST 和 PUT。

GET

首先在 server/app.py 中新增 priceBOOKS 列表的每一個字典元素中:

BOOKS = [
    {
        'id': uuid.uuid4().hex,
        'title': 'On the Road',
        'author': 'Jack Kerouac',
        'read': True,
        'price': '19.99'
    },
    {
        'id': uuid.uuid4().hex,
        'title': 'Harry Potter and the Philosopher\'s Stone',
        'author': 'J. K. Rowling',
        'read': False,
        'price': '9.99'
    },
    {
        'id': uuid.uuid4().hex,
        'title': 'Green Eggs and Ham',
        'author': 'Dr. Seuss',
        'read': True,
        'price': '3.99'
    }
]
複製程式碼

然後,在 Books 元件 client/src/components/Books.vue 中更新表格以顯示購買價格。

<table class="table table-hover">
  <thead>
    <tr>
      <th scope="col">Title</th>
      <th scope="col">Author</th>
      <th scope="col">Read?</th>
      <th scope="col">Purchase Price</th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <tr v-for="(book, index) in books" :key="index">
      <td>{{ book.title }}</td>
      <td>{{ book.author }}</td>
      <td>
        <span v-if="book.read">Yes</span>
        <span v-else>No</span>
      </td>
      <td>${{ book.price }}</td>
      <td>
        <button type="button"
                class="btn btn-warning btn-sm"
                v-b-modal.book-update-modal
                @click="editBook(book)">
            Update
        </button>
        <button type="button"
                class="btn btn-danger btn-sm"
                @click="onDeleteBook(book)">
            Delete
        </button>
      </td>
    </tr>
  </tbody>
</table>
複製程式碼

你現在應該會看到:

default vue app

POST

新增一個新 b-form-groupaddBookModal 中,在 Author 和 read 的 b-form-group 類之間:

<b-form-group id="form-price-group"
              label="Purchase price:"
              label-for="form-price-input">
  <b-form-input id="form-price-input"
                type="number"
                v-model="addBookForm.price"
                required
                placeholder="Enter price">
  </b-form-input>
</b-form-group>
複製程式碼

這個模態現在看起來應該是這樣:

<!-- add book modal -->
<b-modal ref="addBookModal"
         id="book-modal"
        title="Add a new book"
        hide-footer>
  <b-form @submit="onSubmit" @reset="onReset" class="w-100">
    <b-form-group id="form-title-group"
                  label="Title:"
                  label-for="form-title-input">
        <b-form-input id="form-title-input"
                      type="text"
                      v-model="addBookForm.title"
                      required
                      placeholder="Enter title">
        </b-form-input>
    </b-form-group>
    <b-form-group id="form-author-group"
                  label="Author:"
                  label-for="form-author-input">
      <b-form-input id="form-author-input"
                    type="text"
                    v-model="addBookForm.author"
                    required
                    placeholder="Enter author">
      </b-form-input>
    </b-form-group>
    <b-form-group id="form-price-group"
                  label="Purchase price:"
                  label-for="form-price-input">
      <b-form-input id="form-price-input"
                    type="number"
                    v-model="addBookForm.price"
                    required
                    placeholder="Enter price">
      </b-form-input>
    </b-form-group>
    <b-form-group id="form-read-group">
        <b-form-checkbox-group v-model="addBookForm.read" id="form-checks">
          <b-form-checkbox value="true">Read?</b-form-checkbox>
        </b-form-checkbox-group>
    </b-form-group>
    <b-button type="submit" variant="primary">Submit</b-button>
    <b-button type="reset" variant="danger">Reset</b-button>
  </b-form>
</b-modal>
複製程式碼

然後,新增 priceaddBookForm 屬性中:

addBookForm: {
  title: '',
  author: '',
  read: [],
  price: '',
},
複製程式碼

addBookForm 現在和表單的輸入值進行了繫結。想想這意味著什麼。當 addBookForm 被更新時,表單的輸入值也會被更新,反之亦然。以下是 vue-devtools 瀏覽器擴充套件的示例。

state model bind

price 新增到 onSubmit 方法的 payload 中,像這樣:

onSubmit(evt) {
  evt.preventDefault();
  this.$refs.addBookModal.hide();
  let read = false;
  if (this.addBookForm.read[0]) read = true;
  const payload = {
    title: this.addBookForm.title,
    author: this.addBookForm.author,
    read, // property shorthand
    price: this.addBookForm.price,
  };
  this.addBook(payload);
  this.initForm();
},
複製程式碼

更新 initForm 函式,在使用者提交表單點選 "重置" 按鈕後清除已有的值:

initForm() {
  this.addBookForm.title = '';
  this.addBookForm.author = '';
  this.addBookForm.read = [];
  this.addBookForm.price = '';
  this.editForm.id = '';
  this.editForm.title = '';
  this.editForm.author = '';
  this.editForm.read = [];
},
複製程式碼

最後,更新 server/app.py 中的路由:

@app.route('/books', methods=['GET', 'POST'])
def all_books():
    response_object = {'status': 'success'}
    if request.method == 'POST':
        post_data = request.get_json()
        BOOKS.append({
            'id': uuid.uuid4().hex,
            'title': post_data.get('title'),
            'author': post_data.get('author'),
            'read': post_data.get('read'),
            'price': post_data.get('price')
        })
        response_object['message'] = 'Book added!'
    else:
        response_object['books'] = BOOKS
    return jsonify(response_object)
複製程式碼

趕緊測試一下吧!

add book

不要忘了處理客戶端和服務端的錯誤!

PUT

同樣的操作,不過這次是編輯書籍,該你自己動手了:

  1. 新增一個新輸入表單到模態中
  2. 更新屬性中的 editForm 部分
  3. 新增 priceonSubmitUpdate 方法的 payload
  4. 更新 initForm
  5. 更新服務端路由

需要幫助嗎?重新看看前面的章節。或者你可以從 flask-vue-crud 倉庫獲得原始碼。

edit book

訂單頁面

接下來,讓我們新增一個訂單頁面,使用者可以在其中輸入信用卡資訊來購買圖書。

TODO:新增圖片

新增一個購買按鈕

首先給 Books 元件新增一個“購買”按鈕,就在“刪除”按鈕的下方:

<td>
  <button type="button"
          class="btn btn-warning btn-sm"
          v-b-modal.book-update-modal
          @click="editBook(book)">
      Update
  </button>
  <button type="button"
          class="btn btn-danger btn-sm"
          @click="onDeleteBook(book)">
      Delete
  </button>
  <router-link :to="`/order/${book.id}`"
               class="btn btn-primary btn-sm">
      Purchase
  </router-link>
</td>
複製程式碼

這裡,我們使用了 router-link 元件來生成一個連線到 client/src/router/index.js 中的路由的錨點,我們馬上就會用到它。

default vue app

建立模板

新增一個叫做 Order.vue 的新元件檔案到 client/src/components

<template>
  <div class="container">
    <div class="row">
      <div class="col-sm-10">
        <h1>Ready to buy?</h1>
        <hr>
        <router-link to="/" class="btn btn-primary">
          Back Home
        </router-link>
        <br><br><br>
        <div class="row">
          <div class="col-sm-6">
            <div>
              <h4>You are buying:</h4>
              <ul>
                <li>Book Title: <em>Book Title</em></li>
                <li>Amount: <em>$Book Price</em></li>
              </ul>
            </div>
            <div>
              <h4>Use this info for testing:</h4>
              <ul>
                <li>Card Number: 4242424242424242</li>
                <li>CVC Code: any three digits</li>
                <li>Expiration: any date in the future</li>
              </ul>
            </div>
          </div>
          <div class="col-sm-6">
            <h3>One time payment</h3>
            <br>
            <form>
              <div class="form-group">
                <label>Credit Card Info</label>
                <input type="text"
                       class="form-control"
                       placeholder="XXXXXXXXXXXXXXXX"
                       required>
              </div>
              <div class="form-group">
                <input type="text"
                       class="form-control"
                       placeholder="CVC"
                       required>
              </div>
              <div class="form-group">
                <label>Card Expiration Date</label>
                <input type="text"
                       class="form-control"
                       placeholder="MM/YY"
                       required>
              </div>
              <button class="btn btn-primary btn-block">Submit</button>
            </form>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
複製程式碼

你可能會想收集買家的聯絡資訊,比如姓名,郵件地址,送貨地址等等。這就得靠你自己了。

新增路由

client/src/router/index.js

import Vue from 'vue';
import Router from 'vue-router';
import Ping from '@/components/Ping';
import Books from '@/components/Books';
import Order from '@/components/Order';

Vue.use(Router);

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Books',
      component: Books,
    },
    {
      path: '/order/:id',
      name: 'Order',
      component: Order,
    },
    {
      path: '/ping',
      name: 'Ping',
      component: Ping,
    },
  ],
  mode: 'hash',
});
複製程式碼

測試一下。

order page

獲取產品資訊

接下來,讓我們在訂單頁面 上更新書名和金額的佔位符:

order page

回到服務端並更新以下路由介面:

@app.route('/books/<book_id>', methods=['GET', 'PUT', 'DELETE'])
def single_book(book_id):
    response_object = {'status': 'success'}
    if request.method == 'GET':
        # TODO: refactor to a lambda and filter
        return_book = ''
        for book in BOOKS:
            if book['id'] == book_id:
                return_book = book
        response_object['book'] = return_book
    if request.method == 'PUT':
        post_data = request.get_json()
        remove_book(book_id)
        BOOKS.append({
            'id': uuid.uuid4().hex,
            'title': post_data.get('title'),
            'author': post_data.get('author'),
            'read': post_data.get('read'),
            'price': post_data.get('price')
        })
        response_object['message'] = 'Book updated!'
    if request.method == 'DELETE':
        remove_book(book_id)
        response_object['message'] = 'Book removed!'
    return jsonify(response_object)
複製程式碼

我們可以在 script 中使用這個路由向訂單頁面新增書籍資訊:

<script>
import axios from 'axios';

export default {
  data() {
    return {
      book: {
        title: '',
        author: '',
        read: [],
        price: '',
      },
    };
  },
  methods: {
    getBook() {
      const path = `http://localhost:5000/books/${this.$route.params.id}`;
      axios.get(path)
        .then((res) => {
          this.book = res.data.book;
        })
        .catch((error) => {
          // eslint-disable-next-line
          console.error(error);
        });
    },
  },
  created() {
    this.getBook();
  },
};
</script>
複製程式碼

轉到生產環境?你將需要使用環境變數來動態設定基本伺服器端 URL(現在 URL 為 http://localhost:5000)。檢視 文件 獲取更多資訊。

然後,更新 template 中的第一個 ul

<ul>
  <li>Book Title: <em>{{ book.title }}</em></li>
  <li>Amount: <em>${{ book.price }}</em></li>
</ul>
複製程式碼

你現在會看到:

order page

表單驗證

讓我們設定一些基本的表單驗證。

使用 v-model 指令去 繫結 表單輸入值到屬性中:

<form>
  <div class="form-group">
    <label>Credit Card Info</label>
    <input type="text"
           class="form-control"
           placeholder="XXXXXXXXXXXXXXXX"
           v-model="card.number"
           required>
  </div>
  <div class="form-group">
    <input type="text"
           class="form-control"
           placeholder="CVC"
           v-model="card.cvc"
           required>
  </div>
  <div class="form-group">
    <label>Card Expiration Date</label>
    <input type="text"
           class="form-control"
           placeholder="MM/YY"
           v-model="card.exp"
           required>
  </div>
  <button class="btn btn-primary btn-block">Submit</button>
</form>
複製程式碼

新增 card 屬性,就像這樣:

card: {
  number: '',
  cvc: '',
  exp: '',
},
複製程式碼

接下來,更新“提交”按鈕,以便在單擊按鈕時忽略正常的瀏覽器行為,並呼叫 validate 方法:

<button class="btn btn-primary btn-block" @click.prevent="validate">Submit</button>
複製程式碼

將陣列新增到屬性中以儲存驗證錯誤資訊:

data() {
  return {
    book: {
      title: '',
      author: '',
      read: [],
      price: '',
    },
    card: {
      number: '',
      cvc: '',
      exp: '',
    },
    errors: [],
  };
},
複製程式碼

就新增在表單的下方,我們能夠依次顯示所有錯誤:

<div v-show="errors">
  <br>
  <ol class="text-danger">
    <li v-for="(error, index) in errors" :key="index">
      {{ error }}
    </li>
  </ol>
</div>
複製程式碼

新增 validate 方法:

validate() {
  this.errors = [];
  let valid = true;
  if (!this.card.number) {
    valid = false;
    this.errors.push('Card Number is required');
  }
  if (!this.card.cvc) {
    valid = false;
    this.errors.push('CVC is required');
  }
  if (!this.card.exp) {
    valid = false;
    this.errors.push('Expiration date is required');
  }
  if (valid) {
    this.createToken();
  }
},
複製程式碼

由於所有欄位都是必須填入的,而我們只是驗證了每一個欄位是否都有一個值。Stripe 將會驗證下一節你看到的信用卡資訊,所以你不必過度驗證表單資訊。也就是說,只需要保證你自己新增的其他欄位通過驗證。

最後,新增 createToken 方法:

createToken() {
  // eslint-disable-next-line
  console.log('The form is valid!');
},
複製程式碼

測試一下。

form validation

Stripe

如果你沒有 Stripe 賬號的話需要先註冊一個,然後再去獲取你的 測試模式 API Publishable key

stripe dashboard

客戶端

新增 stripePublishableKey 和 stripeCheck(用來禁用提交按鈕)到 data 中:

data() {
  return {
    book: {
      title: '',
      author: '',
      read: [],
      price: '',
    },
    card: {
      number: '',
      cvc: '',
      exp: '',
    },
    errors: [],
    stripePublishableKey: 'pk_test_aIh85FLcNlk7A6B26VZiNj1h',
    stripeCheck: false,
  };
},
複製程式碼

確保新增你自己的 Stripe key 到上述程式碼中。

同樣,如果表單有效,觸發 createToken 方法(通過 Stripe.js)驗證信用卡資訊然後返回一個錯誤資訊(如果無效)或者返回一個 token(如果有效):

createToken() {
  this.stripeCheck = true;
  window.Stripe.setPublishableKey(this.stripePublishableKey);
  window.Stripe.createToken(this.card, (status, response) => {
    if (response.error) {
      this.stripeCheck = false;
      this.errors.push(response.error.message);
      // eslint-disable-next-line
      console.error(response);
    } else {
      // pass
    }
  });
},
複製程式碼

如果沒有錯誤的話,我們就傳送 token 到伺服器,在那裡我們會完成扣費並把使用者轉回主頁:

createToken() {
  this.stripeCheck = true;
  window.Stripe.setPublishableKey(this.stripePublishableKey);
  window.Stripe.createToken(this.card, (status, response) => {
    if (response.error) {
      this.stripeCheck = false;
      this.errors.push(response.error.message);
      // eslint-disable-next-line
      console.error(response);
    } else {
      const payload = {
        book: this.book,
        token: response.id,
      };
      const path = 'http://localhost:5000/charge';
      axios.post(path, payload)
        .then(() => {
          this.$router.push({ path: '/' });
        })
        .catch((error) => {
          // eslint-disable-next-line
          console.error(error);
        });
    }
  });
},
複製程式碼

按照上述程式碼更新 createToken(),然後新增 Stripe.jsclient/index.html 中:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Books!</title>
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
    <script type="text/javascript" src="https://js.stripe.com/v2/"></script>
  </body>
</html>
複製程式碼

Stripe 支援 v2 和 v3(Stripe Elements)版本的 Stripe.js。如果你對 Stripe Elements 和如何把它整合到 Vue 中感興趣,參閱以下資源:1. Stripe Elements 遷移指南 2. 整合 Stripe Elements 和 Vue.js 來建立一個自定義付款表單

現在,當 createToken 被觸發是,stripeCheck 值被更改為 true,為了防止重複收費,我們在 stripeCheck 值為 true 時禁用“提交”按鈕:

<button class="btn btn-primary btn-block"
        @click.prevent="validate"
        :disabled="stripeCheck">
    Submit
</button>
複製程式碼

測試一下 Stripe 驗證的無效反饋:

  1. 信用卡卡號
  2. 安全碼
  3. 有效日期

stripe-form validation

現在,讓我們開始設定服務端路由。

服務端

安裝 Stripe 庫:

$ pip install stripe==1.82.1
複製程式碼

新增路由介面:

@app.route('/charge', methods=['POST'])
def create_charge():
    post_data = request.get_json()
    amount = round(float(post_data.get('book')['price']) * 100)
    stripe.api_key = os.environ.get('STRIPE_SECRET_KEY')
    charge = stripe.Charge.create(
        amount=amount,
        currency='usd',
        card=post_data.get('token'),
        description=post_data.get('book')['title']
    )
    response_object = {
        'status': 'success',
        'charge': charge
    }
    return jsonify(response_object), 200
複製程式碼

在這裡設定書籍價格(轉換為美分),專有 token(來自客戶端的 createToken 方法),以及書名,然後我們利用 API Secret key 生成一個新的 Stripe 賬單。

瞭解更多建立賬單的資訊,參考官方 API 文件

Update the imports:

import os
import uuid

import stripe
from flask import Flask, jsonify, request
from flask_cors import CORS
複製程式碼

獲取 測試模式 API Secret key

stripe dashboard

把它設定成一個環境變數:

$ export STRIPE_SECRET_KEY=sk_test_io02FXL17hrn2TNvffanlMSy
複製程式碼

確保使用的是你自己的 Stripe key!

測試一下吧!

purchase a book

Stripe Dashboard 中你應該會看到購買記錄:

stripe dashboard

你可能還想建立 顧客,而不僅僅是建立賬單。這樣一來有諸多優點。你能同時購買多個物品,以便跟蹤客戶購買記錄。你可以向經常購買的使用者提供優惠,或者向許久未購買的使用者聯絡,還有許多用處這裡就不做介紹了。它還可以用來防止欺詐。參考以下 Flask 專案 來看看如何新增客戶。

訂單完成頁面

比起把買家直接轉回主頁,我們更應該把他們重定向到一個訂單完成頁面,以感謝他們的購買。

新增一個叫 OrderComplete.vue 的新元件檔案到 “client/src/components” 中:

<template>
  <div class="container">
    <div class="row">
      <div class="col-sm-10">
        <h1>Thanks for purchasing!</h1>
        <hr><br>
        <router-link to="/" class="btn btn-primary btn-sm">Back Home</router-link>
      </div>
    </div>
  </div>
</template>
複製程式碼

更新路由:

import Vue from 'vue';
import Router from 'vue-router';
import Ping from '@/components/Ping';
import Books from '@/components/Books';
import Order from '@/components/Order';
import OrderComplete from '@/components/OrderComplete';

Vue.use(Router);

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Books',
      component: Books,
    },
    {
      path: '/order/:id',
      name: 'Order',
      component: Order,
    },
    {
      path: '/complete',
      name: 'OrderComplete',
      component: OrderComplete,
    },
    {
      path: '/ping',
      name: 'Ping',
      component: Ping,
    },
  ],
  mode: 'hash',
});
複製程式碼

createToken 方法中更新重定向:

createToken() {
  this.stripeCheck = true;
  window.Stripe.setPublishableKey(this.stripePublishableKey);
  window.Stripe.createToken(this.card, (status, response) => {
    if (response.error) {
      this.stripeCheck = false;
      this.errors.push(response.error.message);
      // eslint-disable-next-line
      console.error(response);
    } else {
      const payload = {
        book: this.book,
        token: response.id,
      };
      const path = 'http://localhost:5000/charge';
      axios.post(path, payload)
        .then(() => {
          this.$router.push({ path: '/complete' });
        })
        .catch((error) => {
          // eslint-disable-next-line
          console.error(error);
        });
    }
  });
},
複製程式碼

final app

最後,你還可以在訂單完成頁面顯示客戶剛剛購買的書籍的(標題,金額,等等)。

獲取唯一的賬單 ID 然後傳遞給 path

createToken() {
  this.stripeCheck = true;
  window.Stripe.setPublishableKey(this.stripePublishableKey);
  window.Stripe.createToken(this.card, (status, response) => {
    if (response.error) {
      this.stripeCheck = false;
      this.errors.push(response.error.message);
      // eslint-disable-next-line
      console.error(response);
    } else {
      const payload = {
        book: this.book,
        token: response.id,
      };
      const path = 'http://localhost:5000/charge';
      axios.post(path, payload)
        .then((res) => {
          // updates
          this.$router.push({ path: `/complete/${res.data.charge.id}` });
        })
        .catch((error) => {
          // eslint-disable-next-line
          console.error(error);
        });
    }
  });
},
複製程式碼

更新客戶端路由:

{
  path: '/complete/:id',
  name: 'OrderComplete',
  component: OrderComplete,
},
複製程式碼

然後,在 OrderComplete.vue 中,從 URL 中獲取賬單 ID 併傳送到服務端:

<script>
import axios from 'axios';

export default {
  data() {
    return {
      book: '',
    };
  },
  methods: {
    getChargeInfo() {
      const path = `http://localhost:5000/charge/${this.$route.params.id}`;
      axios.get(path)
        .then((res) => {
          this.book = res.data.charge.description;
        })
        .catch((error) => {
          // eslint-disable-next-line
          console.error(error);
        });
    },
  },
  created() {
    this.getChargeInfo();
  },
};
</script>
複製程式碼

在伺服器上配置新路由來 檢索 賬單:

@app.route('/charge/<charge_id>')
def get_charge(charge_id):
    stripe.api_key = os.environ.get('STRIPE_SECRET_KEY')
    response_object = {
        'status': 'success',
        'charge': stripe.Charge.retrieve(charge_id)
    }
    return jsonify(response_object), 200
複製程式碼

最後,在 template 中更新 <h1></h1>

<h1>Thanks for purchasing - {{ this.book }}!</h1>
複製程式碼

最後一次測試。

總結

完成了!一定要從最開始進行閱讀。你可以在 GitHub 中的 flask-vue-crud 倉庫找到原始碼。

想挑戰更多?

  1. 新增客戶端和服務端的單元和整合測試。
  2. 建立一個購物車以方便顧客能夠一次購買多本書。
  3. 使用 Postgres 來儲存書籍和訂單。
  4. 使用 Docker 整合 Vue 和 Flask(以及 Postgres,如果你加入了的話)來簡化開發工作流程。
  5. 給書籍新增圖片來建立一個更好的產品頁面。
  6. 獲取 email 然後傳送 email 確認郵件(查閱 使用 Flask、Redis Queue 和 Amazon SES 傳送確認電子郵件)。
  7. 部署客戶端靜態檔案到 AWS S3 然後部署服務端應用到一臺 EC2 例項。
  8. 投入生產環境?思考一個最好的更新 Stripe key 的方法,讓它們基於環境動態更新。
  9. 建立一個分離元件來退訂。

如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章