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

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

這是一個進階教程。咱們默認您已經基本掌握了 Vue.js 和 Flask。若是你尚未了解過它們,請查看下面的連接以瞭解更多:html

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

最終效果前端

final app

主要依賴vue

  • 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

目錄

目的

在本教程結束的時候,你可以...java

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

項目安裝

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

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

搭建並激活一個虛擬環境,而後運行 Flask 應用:android

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

上述搭建環境的命令可能因操做系統和運行環境而異。webpack

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

"pong!"
複製代碼

而後,安裝依賴並在另外一個終端中運行 Vue 應用:git

$ 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前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索