Skip to content

Instantly share code, notes, and snippets.

@kiennt
Last active November 22, 2022 02:17
Show Gist options
  • Save kiennt/4c1ae78ec287641314de54a35eac164f to your computer and use it in GitHub Desktop.
Save kiennt/4c1ae78ec287641314de54a35eac164f to your computer and use it in GitHub Desktop.
Stripe API spec

Các yêu cầu chung của API

0. Vì sao cần viết tài liệu này

Tài liệu này viết ra nhằm mục đích giúp cho

  • Xác định các định dạng cơ bản của các API
  • Lưu trữ lại các luồng dữ liệu của phần thanh toán
  • Mô tả chi tiết từng API cho hệ thống Backend phần thanh toán

Tài liệu bao gồm các phần chính sau

  1. Định dạng dữ liệu đầu vào của API

  2. Định dạng dữ liệu trả về của API

  3. Phương thức xác thực user

  4. Các bảng mã lỗi

  5. Các kiểu dữ liệu

  6. Các API chi tiết

    6.1. API module User

    6.2. API module Order

1. Định dạng dữ liệu đầu vào của API

Client muốn gọi API cần truyền vào

  • phương thức của HTTP: cụ thể là các phương thức GET, POST, PUT, DELETE
  • địa chỉ URI của API: ví dụ địa chỉ /v1/users
  • tham số truy vấn (query params) trên URI: ví dụ /v1/users?access_token=abc
  • dữ liệu trong POST:

API được phân chia version bằng cách đánh số version ngay trên URI

VD: API version 1, sẽ có tiền tố của URI là v1

2. Định dạng dữ liệu trả về của API

Mọi API đều trả về với status code là 200, nội dung của dữ liệu trả về sẽ có định dang như sau

{
    "data": <data>
    "error": {
        "code": <error_code>,
        "message": <error_message>
    }
}

Để phân biệt xem API đã được thực hiện thành công hay không. Client kiểm tra xem dữ liệu trả về có trường error không?

Nếu trường error không được trả về, tức là API đã thực hiện thành công. Client có thể lấy dữ liệu từ trường data.

Tuy nhiên, nếu API không thể thực hiện được (vì nhiều nguyên nhân như (1) client gửi lên dữ liệu không chính xác (2) client chưa xác thực user (3) client không có quyền thực hiện API hoặc (4) có lỗi xảy ra nội tại bên trong server), thì trường error sẽ được.

Dữ liệu của trường error có định dạng như sau

{
    "code": <error_code>
    "message": <error_message>
}

Trong đó

  • error_code: là chuỗi mô tả mã lỗi
  • error_message: là chuỗi mô tả lỗi

Mã lỗi và nội dung của lỗi sẽ được mô tả trong tài liệu [Các bảng mã lỗi]

3. Phương thức để xác thực user

Để xác thực xem user đang thực hiện API là user nào, client cần truyền vào một tham số truy vấn có tên là access_token

Phía server dựa vào access_token sẽ xác định được user đang thực hiện request là user nào.

3.1. Phương thức xác thực đơn giản phía client:

Đối với ứng dụng Impress, do chưa có hệ thống SignUp/Login, nên client sẽ tự cài đặt một phương thức xác thực đơn giản.

3.1.1. Phạm vi:

  • Coi tất cả các user sử dụng chung một thiết bị là một user
  • Khi ứng dụng bị xóa khỏi thiết bị, nếu ứng dụng được cài lại, sẽ coi như ứng dụng thuộc về một user mới

3.1.2. Chi tiết cách cài đặt

  • (1) Khi lần đầu tiên user mở ứng dụng version mới lên, nếu ứng dụng chưa có access token, ứng dụng sẽ tự động tạo ra một UUID. UUID này sẽ được coi là access token lạc từ đây về sau của ứng dụng
  • (2) Ứng dụng tự động lưu trữ lại access token vào một bộ nhớ trên thiết bị. Bộ nhớ này cần đảm bảo là khi ứng dụng bị xóa, thì bộ nhớ này cũng bị xóa theo.
  • (3) Từ đây về sau, mỗi lần ứng dụng gọi API từ server, ứng dụng sẽ sử dụng access token tạo được từ bước 1 để gọi API.

3.1.3. Các phiên bản mới:

Phương thức xác thực phía client có thể cải tiến cho các phiên bản mới. Cụ thể, ứng dụng có thể làm các chức năng để cho phép user đăng ký, đăng nhập và sửa profile.

4. Bảng mã lỗi

code message description
D0001 invalid data dữ liệu truyền lên không đúng yêu cầu của server
A0001 invalid access token access token truyền lên không hợp lệ
O0001 coupon does not exist mã coupon không tồn tại
O0002 expired coupon mã coupon đã quá hạn sử dụng
O0003 coupon usage limit exceed mã coupon đã bị sử dụng vượt quá số lượng cho phép
O0004 payment gateway error lỗi khi gọi API sang stripe
O0005 invalid sku mã sku không tồn tại
O0006 invalid product type product type không tồn tại

5. Các kiểu dữ liệu

5.1. User

field description type requirement
id id của user string
last_recpipient thông tin địa chỉ để ship hàng tới của lần order gần nhất recipient thông tin này có thể trả về null
last_stripe_customer_id id của strip_customer trong lần order gần đây nhất string nếu user chưa order thì trả về null
last_stripe_ephemeral_key stripe ephemeral key string

5.2. Recipient

field description type requirement
full_name tên string không được để trống
country quốc gia string không được đế trống
state bang string không được để trống
city thành phố string không được để trống
street_address địa chỉ string không được để trống
street_address_2 địa chỉ 2 string không được để trống
zip_code mẫ zip string không được để trống
phone số điện thoại string không được để trống
email email string không được để trống

5.3. Price

field description type requirement
raw_price giá của sản phẩm float giá của sản phẩm được tính bằng cent
shipping_price phí ship sản phẩm float
sale_tax_price thuế của sản phẩm float
shipping_tax thuế của shipping float
total_price tổng tiền sau khi có cả thuế và tiền ship float

6. Các module API chính

  • User Module: các module liên quan tới việc tương tác với User
  • Order Module: các module liên quan tới việc tạo order

6.1. User Module

6.1.1. Lấy thông tin user hiện tại

GET /v4/me

Query params:

field description type requirement
access_token access token được sinh ra dựa theo tài liệu mô tả ở phần Phương thức xác thực user string không được để trống

Response

Trả về 1 user object

Các lỗi có thể xảy ra

mã lỗi hoàn cảnh xảy ra
A0001 access token truyền lên không tồn tại

Các bước cần làm:

  1. Kiểm tra user đang thực hiện request
  2. Lấy ra order gần nhất của user

Ví dụ:

curl -H "Content-type: application/json" -XGET "http://localhost:8000/v4/me/?access_token=c9aa71a8-1ebc-4d8b-b54b-c88434607cda"

{
    "data": {
        "id": "c9aa71a8-1ebc-4d8b-b54b-c88434607cda",
        "last_recipient": {
            "id": 539,
            "full_name": "Kien Nguyen",
            "country": "Viet nam",
            "state": "HCM",
            "city": "HCM",
            "email": "test@test.com",
            "phone": "+111111111",
            "street_address": "",
            "street_address_2": "",
            "zip_code": "10000"
        },
        "last_stripe_customer_id": "cus_CkxqMfmzeLPwa1",
        "last_stripe_ephemeral_key": {
            "id": "ephkey_1CLSbFLbKnetRaZ0FR0rbE1J",
            "associated_objects": [
                {
                    "id": "cus_CkxqMfmzeLPwa1",
                    "type": "customer"
                }
            ],
            "object": "ephemeral_key",
            "created": 1524820241,
            "expires": 1524823841,
            "livemode": false,
            "secret": "ek_test_YWNjdF8xN29NajFMYktuZXRSYVowLDNQdUZjT2JtR3dsQnpHa2k2TktkSTJFb25PNHJBcEw"
        }
    }
}

6.2. Order Module

6.2.2. Lấy giá của một sản phẩm

POST /v4/prices

Query params:

field description type requirement
access_token access token được sinh ra dựa theo tài liệu mô tả ở phần Phương thức xác thực user string không được để trống

Post data

field description type requirement
recipient địa chỉ nhận hàng recipient không được để trống
sku mã sku của sản phẩm string không được để trống
coupon mã giảm giá string mã giảm giá là một chuỗi dưới 100 ký tự, tất cả các ký tự đều được viết dưới dạng in thường

Dữ liệu trả về một price object

Các lỗi có thể xảy ra

mã lỗi hoàn cảnh xảy ra
A0001 access token truyền lên không tồn tại
O0001 mã coupon không tồn tại
O0002 mã coupon đã quá hạn sử dụng
O0003 mã coupon đã bị sử dụng vượt quá số lượng cho phép
O0004 lỗi khi gọi API sang stripe
O0005 mã sku không tồn tại

Các bước cần làm:

  1. Kiểm tra user đang thực hiện request

  2. Nếu user chưa có trong hệ thống, thì tạo mới user

  3. Kiểm tra mã coupon xem có sử dụng đươc với user không

  4. Tạo mới một Order trong Stripe cho Customer của stripe

    4.1. Lấy giá của SKU từ Stripe (https://stripe.com/docs/api#retrieve_sku)

    4.2. Tính discount price từ SKU và coupon (dựa vào công thức tính giá của coupon)

    4.3. Tạo mới order dựa vào SKU và giá discount price (https://stripe.com/docs/api#create_order)

Các trường metadata cần thêm vào Order:

+ sku: sku 

+ coupon: nếu có

+ user_id: id của user trong hệ thống backend
  1. Lưu lại price trong bộ nhớ RAM, xóa đi stripe Order.
  2. Trả về dữ liệu cho client

Ví dụ

curl -H "Content-type: application/json" -XPOST "http://localhost:8000/v4/prices/?access_token=c9aa71a8-1ebc-4d8b-b54b-c88434607cda" -d '
{
    "product_type": 1,
    "stripe_token": "tok_mastercard",
    "sku": "sku_C5I3oDU0OIfKRk",
    "recipient": {
        "full_name": "Kien Nguyen",
        "country": "Viet nam",
        "city": "HCM",
        "state": "HCM",
        "street_address": "",
        "street_address_2": "",
        "email": "test@test.com",
        "phone": "+111111111",
        "zip_code": "10000"
    },
    "pdf_url": "a\nb",
    "coupon": "ip1"
}
'

{
    "data": {
        "discount_price": 799,
        "raw_price": 7999,
        "sale_tax": 0,
        "shipping_price": 550,
        "shipping_tax": 0,
        "total_price": 7750
    }
}

6.2.3. Order một sản phẩm

POST /v4/orders

Query params:

field description type requirement
access_token access token được sinh ra dựa theo tài liệu mô tả ở phần Phương thức xác thực user string không được để trống

Post data

field description type requirement
product_type loại sản phẩm integer không được để trống
meta thông tin của từng sản phẩm dictionary không được để trống
recipient địa chỉ nhận hàng recipient không được để trống
sku mã sku của sản phẩm string không được để trống
coupon mã giảm giá string mã giảm giá là một chuỗi dưới 100 ký tự, tất cả các ký tự đều được viết dưới dạng in thường
stripe_customer_id mã khách hàng của stripe string
stripe_token token của credit card string

Dữ liệu trả về một Order object

Các lỗi có thể xảy ra

mã lỗi hoàn cảnh xảy ra
A0001 access token truyền lên không tồn tại
O0001 mã coupon không tồn tại
O0002 mã coupon đã quá hạn sử dụng
O0003 mã coupon đã bị sử dụng vượt quá số lượng cho phép
O0004 lỗi khi gọi API sang stripe
O0005 mã sku không tồn tại
O0006 product type không tồn tại

Các bước cần làm:

  1. Kiểm tra user đang thực hiện request

  2. Nếu user chưa có trong hệ thống, thì tạo mới user

  3. Kiểm tra mã coupon xem có sử dụng đươc với user không, nếu mã không hợp lệ trả về lỗi

  4. Nếu user gửi lên stripe_token, tạo mới một Customer trong Stripe

  5. Tạo mới một Order trong Stripe cho Customer của stripe

    5.1. Lấy giá của SKU từ Stripe (https://stripe.com/docs/api#retrieve_sku)

    5.2. Tính discount price từ SKU và coupon (dựa vào công thức tính giá của coupon)

    5.3. Tạo mới order dựa vào SKU và giá discount price (https://stripe.com/docs/api#create_order)

Các trường metadata cần thêm vào Order:

+ sku: sku 
+ coupon: nếu có
+ user_id: id của user trong hệ thống backend
  1. Charge tiền order (https://stripe.com/docs/api#pay_order)
  2. Tạo mới Order trong hệ thống backend
  3. Trả về dữ liệu cho client

Ví dụ

curl -H "Content-type: application/json" -XPOST "http://localhost:8000/v4/orders/?access_token=c9aa71a8-1ebc-4d8b-b54b-c88434607cda" -d '
{
    "product_type": 1,
    "stripe_token": "tok_mastercard",
    "sku": "sku_C5I3oDU0OIfKRk",
    "recipient": {
        "full_name": "Kien Nguyen",
        "country": "Viet nam",
        "city": "HCM",
        "state": "HCM",
        "street_address": "",
        "street_address_2": "",
        "email": "test@test.com",
        "phone": "+111111111",
        "zip_code": "10000"
    },
    "pdf_url": "a\nb",
    "coupon": "ip1"
}
'

{
    "data": {
        "coupon": "ip1",
        "pdf_url": "a\nb",
        "product_type": 1,
        "recipient": {
            "city": "HCM",
            "country": "Viet nam",
            "email": "test@test.com",
            "full_name": "Kien Nguyen",
            "phone": "+111111111",
            "state": "HCM",
            "street_address": "",
            "street_address_2": "",
            "zip_code": "10000"
        },
        "sku": "sku_C5I3oDU0OIfKRk",
        "stripe_token": "tok_mastercard"
    }
}

7. Các tham số để test

tên tham số mô tả giá trị
stripe_api_key API key của stripe pk_test_YZJjZfSnRTqoBmR0PIN8HOv3
stripe_api_secret API secret của stripe sk_test_AUSBqafUlNOVQkcNaSOprHBo
sku sku để test sku_C5I3oDU0OIfKRk
request_data du lieu de request {
"sku": "sku_C5I3oDU0OIfKRk",
"recipient": {
  "full_name": "Kien Nguyen Trung",
  "country": "Vietnam",
  "state": "HCM",
  "city": "Ho Chi Minh",
  "street_address": "hello there",
  "street_address_2": "hello there2",
  "zip_code": "12345",

} } |

Issue này nhằm mục đích tổng hợp các tài liệu cần sử dụng trong project

Document

Stripe Order spec

https://stripe.com/docs/orders/guide

Flow to fetch ephemeral key

Document for Stripe SDK https://stripe.com/docs/mobile/ios/standard

image

  • client generate uuid in its side, and store the generated uuid in client local storage
  • client send uudi to server
  • server will create a stripe customer for the uuid if the uuid is not belong to any stripe customer on server
  • server also generate a stripe ephemeral key and return to client

Flow to fetch price

image

  • client fetches all SKUs from Stripe by using restricted api key. This restricted API key is created inside stripe dashboard. We are using key with name iOS SKU for live and iOS SKU test for staging Client uses API which is listed at https://stripe.com/docs/api/curl#list_skus to get SKU

Key for testing: rk_test_l4lPywaR8nhZZwyb1bGq2PBK

curl https://api.stripe.com/v1/skus?limit=3 \
   -u <restricted_api_key>: 
  • server return list of SKUs
  • when user enter all of her/his shipping address, client sends customer_id, sku_id, shipping address and may be coupon (optional) to server
  • server will create a stripe order, and link it with customer
  • stripe returns to server a stripe order with price.
  • server returns price to client

Flow to create order

image

Client using this code to send stripe token. stripe token is source.stripeID

- (void)paymentContext:(STPPaymentContext *)paymentContext
didCreatePaymentResult:(STPPaymentResult *)paymentResult
            completion:(STPErrorBlock)completion {
    [self.apiClient createCharge:paymentResult.source.stripeID completion:^(NSError *error) {
        if (error) {
            completion(error);
        } else {
            completion(nil);
        }
    }];
}
  • client sends sku_id, shipping_address, customer_id, stripe_token and coupon (optional).
  • server call stripe to add source to customer
  • server all call stripe to get price
  • after that, server create a charge which linked to customer and source (stripe_token)
  • stripe return status of charge to server
  • server forward the status to user

Code to create order with coupon

import stripe
stripe.api_key = "sk_test_AUSBqafUlNOVQkcNaSOprHBo"

stripe.Order.create(
    currency='usd',
    items=[
        {
            "type": 'sku',
            "parent": 'sku_C5I3oDU0OIfKRk'
        },
        {
            "type": 'discount',
            "description": "discount",
            "currency": "usd",
            "amount": -10,
        }
    ],
    shipping={
        "name": 'Alexander Harris',
        "address": {
            "line1": '1234 Main Street',
            "city": 'San Francisco',
            "state": 'CA',
            "country": 'US',
            "postal_code": '94111'
        },
    },
    email='alexander.harris@example.com'
)

Spec

https://gist.github.com/kiennt/4c1ae78ec287641314de54a35eac164f

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment