Browse Source

add confirm/payments

conrad 3 years ago
parent
commit
02e647d54e
8 changed files with 257 additions and 2 deletions
  1. 5 0
      .gitignore
  2. 18 0
      .vscode/launch.json
  3. 0 0
      __init__.py
  4. 0 2
      aa.txt
  5. 75 0
      linepay_test.py
  6. 109 0
      main.py
  7. 28 0
      templates/confirm.html
  8. 22 0
      templates/request.html

+ 5 - 0
.gitignore

@@ -127,3 +127,8 @@ dmypy.json
 
 # Pyre type checker
 .pyre/
+/env/*
+linepay_test.py
+/__pycache__/*
+/.vscode/*
+# /templates/*

+ 18 - 0
.vscode/launch.json

@@ -0,0 +1,18 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Python: FastAPI",
+            "type": "python",
+            "request": "launch",
+            "module": "uvicorn",
+            "args": [
+                "main:app"
+            ],
+            "jinja": true
+        }
+    ]
+}

+ 0 - 0
__init__.py


+ 0 - 2
aa.txt

@@ -1,2 +0,0 @@
-111
-

+ 75 - 0
linepay_test.py

@@ -0,0 +1,75 @@
+import requests
+import json
+import base64
+import hashlib
+import hmac
+import uuid
+
+# sandbox
+url = "https://sandbox-api-pay.line.me"
+# url_path = "/v3/payments/request"
+transactionId=2021101400692705810
+orderId=1
+url_path = "/v3/payments/" + str(transactionId) + "/confirm"
+
+# # production
+# url = "https://api-pay.line.me"
+ChannelId = 1656522171
+ChannelSecret = 'd6d37304bc39496025519366b9c0ab44'
+nonce = uuid.uuid4()
+
+def line_Signature(ChannelSecret, payload, nonce, url_path):
+  signature = hmac.new(
+      ChannelSecret.encode('utf-8'),
+      msg=(ChannelSecret + url_path + json.dumps(payload) + str(nonce)).encode('utf-8'),
+      digestmod=hashlib.sha256
+    ).digest()
+  return base64.b64encode(signature)
+
+
+
+# payload={
+#     "amount": 100,
+#     "currency": "TWD",
+#     "orderId":1,
+#     "packages" : [
+#         {
+#             "id" : "1",
+#             "amount": 100,
+#             "products" : [
+#                 {
+#                     "id" : "PEN-B-001",
+#                     "name" : "Pen Brown",
+#                     "imageUrl" : "https://pay-store.line.com/images/pen_brown.jpg",
+#                     "quantity" : 2,
+#                     "price" : 50
+#                 }
+#             ]
+#         }
+#     ],
+#     "redirectUrls" : {
+#         "confirmUrl" : "https://pay-store.line.com/order/payment/confirm",
+#         "cancelUrl" : "https://pay-store.line.com/order/payment/cancel"
+#     }
+# }
+payload={
+    "amount": 100,
+    "currency": "TWD"
+ }
+
+
+
+headers = {
+  'Content-Type': 'application/json',
+  'X-LINE-ChannelId': str(ChannelId),
+  'X-LINE-Authorization-Nonce': str(nonce),
+  'X-LINE-Authorization': line_Signature(ChannelSecret, payload, nonce, url_path)
+}
+
+response = requests.request("POST", url + url_path, headers=headers, data=json.dumps(payload))
+
+
+
+
+
+print(response.text)

+ 109 - 0
main.py

@@ -0,0 +1,109 @@
+import dotenv
+import uuid
+import os
+import logging
+from typing import AsyncContextManager, Optional
+from fastapi import FastAPI, Request, responses
+from fastapi.templating import Jinja2Templates
+from pydantic import BaseModel
+from dotenv import load_dotenv
+from os.path import join, dirname
+from linepay import LinePayApi
+from starlette.responses import HTMLResponse
+
+# dotenv
+dotenv_path = join(dirname(__file__),'./env/.env')
+load_dotenv(dotenv_path)
+
+# logger (TBD)
+
+# template
+templates = Jinja2Templates(directory="templates")
+
+# Line Pay Config
+LINE_PAY_CHANNEL_ID = os.environ.get("LINE_PAY_CHANNEL_ID")
+LINE_PAY_CHANNEL_SECRET = os.environ.get("LINE_PAY_CHANNEL_SECRET")
+LINE_PAY_REQEST_BASE_URL = "https://{}".format(os.environ.get("HOST_NAME"))
+line = LinePayApi(LINE_PAY_CHANNEL_ID, LINE_PAY_CHANNEL_SECRET, is_sandbox=True)
+
+# CACHE
+CACHE = {}
+
+
+# Fastapi
+app = FastAPI()
+
+
+
+@app.get('/')
+def hellow():
+    return {"Hello" : "World"}
+
+## Request
+@app.get('/request', response_class=HTMLResponse)
+async def pay_request(request: Request):
+    order_id = str(uuid.uuid4())
+    amount = 1200
+    currency = "TWD"
+    CACHE["order_id"] = order_id
+    CACHE["amount"] = amount
+    CACHE["currency"] = currency
+    request_options ={
+        "amount" : amount,
+        "currency" : currency,
+        "orderId" : order_id,
+        "packages" : [
+            {
+                "id" : "早鳥方案",
+                "amount" : 1200,
+                "products" :[
+                    {
+                        # "id" : "Id_早鳥方案",
+                        "name" : "早鳥方案",
+                        "quantity" : 1,
+                        "price" : 1200,
+                        "imageUrl" : "https://kb.rspca.org.au/wp-content/uploads/2018/11/golder-retriever-puppy.jpeg"
+                    }
+                ]
+            }
+
+        ],
+        "redirectUrls" : {
+            "confirmUrl" : LINE_PAY_REQEST_BASE_URL + "/confirm/",
+            "cancelUrl" : LINE_PAY_REQEST_BASE_URL + "/cancel/"
+        }
+    }
+    response = line.request(request_options)
+    transaction_id = int(response.get("info",{}).get("transactionId",0))
+    check_result = line.check_payment_status(transaction_id)
+    response["transaction_id"] = transaction_id
+    response["paymentStatusCheckReturnCode"] = check_result.get("returnCode", None)
+    response["paymentStatusCheckReturnMessage"] = check_result.get("returnMessage", None)
+    return templates.TemplateResponse("request.html", {"request":response})
+    # return response
+
+
+## Confirm
+@app.get('/confirm/')
+async def pay_confirm(transactionId: int, orderId: Optional[str] = None):
+    CACHE["transaction_id"] = transactionId
+    response = line.confirm(transactionId,float(CACHE.get("amount",0)),CACHE.get("currency","TWD"))
+    check_result = line.check_payment_status(transactionId)
+    payment_details = line.payment_details(transaction_id=transactionId)
+    response["transaction_id"] = transactionId
+    response["paymentStatusCheckReturnCode"] = check_result.get("returnCode", None)
+    response["paymentStatusCheckReturnMessage"] = check_result.get("returnMessage",None)
+    response["payment_details"] = payment_details
+    return templates.TemplateResponse("confirm.html", {"request":response})
+    # return {"transactionId" : str(transactionId), "orderId" : orderId}
+## Capture
+
+## Refund
+
+## Payment Details API
+@app.get('/payments')
+async def pay_payments(orderId : str):
+    payment_details = line.payment_details(order_id=orderId)
+    return payment_details
+
+## Pay Preapproved API

+ 28 - 0
templates/confirm.html

@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+    <head>
+		<title>LINE Pay Confirm API request</title>
+    </head>
+    <body>
+		<dl>
+			<dt>orderId</dt>
+			<dd>{{ request.info.orderId }}</dd>
+			<dt>transactionId</dt>
+			<dd>{{ request.info.transactionId }}</dd>
+			<dt>product name</dt>
+			<dd>{{ request.info.packages[0].products[0].name }}</dd>
+			<dt>price</dt>
+			<dd>{{ request.info.packages[0].products[0].price }}</dd>
+			<dt>Payment Status Check Return Code</dt>
+			<dd>{{ request.paymentStatusCheckReturnCode }}</dd>
+			<dt>Payment Status Check Return Message</dt>
+			<dd>{{ request.paymentStatusCheckReturnMessage }}</dd>
+			<dt>Payment Details</dt>
+			<dd>{{ request.payment_details }}</dd>
+			<dt>Access to below link to refund your payment</dt>
+			<dd>
+				<a href="/refund" rel="noopener noreferrer">REFUND</a>
+			</dd>
+		</dl>
+    </body>
+</html>

+ 22 - 0
templates/request.html

@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+    <head>
+		<title>LINE Pay Request API</title>
+    </head>
+    <body>
+		<dl>
+			<dt>paymentAccessToken</dt>
+			<dd>{{ request.info.paymentAccessToken }}</dd>
+			<dt>transactionId</dt>
+			<dd>{{ request.info.transactionId }}</dd>
+			<dt>Payment Status Check Return Code</dt>
+			<dd>{{ request.paymentStatusCheckReturnCode }}</dd>
+			<dt>Payment Status Check Return Message</dt>
+			<dd>{{ request.paymentStatusCheckReturnMessage }}</dd>
+			<dt>Access to below paymentUrl to confirm your payment</dt>
+			<dd>
+				<a href="{{ request.info.paymentUrl.web }}" rel="noopener noreferrer">paymentUrl</a>
+			</dd>
+		</dl>
+    </body>
+</html>