Go支付中臺方案:多平臺相容與多專案對接

王中阳Go發表於2024-12-10

一、中臺的概念

中臺是一種企業級的架構模式,它處於前臺應用和後臺資源之間,將企業核心能力進行整合、封裝,形成一系列可複用的業務能力元件。這些元件就像樂高積木一樣,可以被不同的前臺業務快速呼叫,從而避免重複開發,提高業務創新和響應速度。中臺的核心思想是資料共享、能力複用,透過將通用的業務邏輯和資料處理抽取出來,為企業的數字化轉型提供堅實的架構基礎。

二、使用支付中臺的意義和價值

(一)提高支付系統的複用性

在企業的業務場景中,往往存在多個需要支付功能的專案,如各種型別的電商應用、遊戲專案、企業內部的管理系統等。如果沒有支付中臺,每個專案都需要獨立開發支付模組,這不僅會導致大量的重複工作,而且不同專案中的支付模組可能存在差異,增加了維護成本和難度。而支付中臺將支付相關的核心功能進行統一封裝,各個專案可以直接複用這些功能,大大提高了開發效率,減少了程式碼冗餘。

(二)提升支付業務的敏捷性和創新能力

市場環境變化迅速,支付方式和業務需求也在不斷更新。使用支付中臺,企業可以在中臺層面快速響應這些變化,對支付功能進行升級和最佳化,而無需在每個使用支付功能的專案中逐一修改。例如,當新的支付平臺出現或者支付安全標準提高時,只需在支付中臺進行調整,所有依賴中臺的專案都能受益。這種敏捷性使得企業能夠更快地推出新的支付業務模式,滿足使用者多樣化的需求,增強市場競爭力。

(三)保障支付資料的一致性和安全性

支付資料的處理和儲存是支付系統的關鍵環節。不同專案獨立處理支付資料可能會導致資料不一致的問題,例如訂單狀態在不同系統中的不同步。支付中臺透過統一的資料管理和處理機制,確保支付資料在各個專案中的一致性。同時,中臺可以集中實施安全策略,如對支付請求的加密、對支付平臺 API 金鑰的安全儲存和管理等,有效降低支付安全風險,保護使用者和企業的利益。

(四)降低系統的複雜性和耦合度

在沒有中臺的情況下,各個專案與不同支付平臺的對接會使整個系統變得非常複雜,專案之間以及專案與支付平臺之間的耦合度很高。支付中臺作為一箇中間層,將專案與支付平臺解耦,對外提供統一的支付介面給各個專案,對內負責與不同支付平臺的對接和互動。這樣,每個專案只需要關注自身的業務邏輯和與支付中臺的互動,無需瞭解支付平臺的具體細節,降低了系統的複雜性,提高了系統的可維護性。

三、支付中臺的設計目標

(一)多支付平臺支援

全面支援微信支付、支付寶支付等主流支付方式。這要求系統能夠精確處理不同支付平臺的支付請求,包括但不限於引數解析、簽名驗證、發起支付以及處理支付回撥等操作,從而確保支付流程的順暢性和安全性,為使用者提供可靠的支付體驗。

(二)多專案對接能力

  1. 針對其他 Go 應用專案,提供簡潔、規範且易於整合的介面。這些介面應充分遵循 Go 語言的程式設計風格和設計模式,確保程式碼的可讀性、可維護性以及可擴充套件性,方便 Go 應用快速接入支付功能。
  2. 對於遊戲專案,考慮到遊戲內支付具有小額高頻、與虛擬貨幣系統互動等特殊需求,支付中臺需具備高度的靈活性,能夠在不影響遊戲流暢性和使用者體驗的前提下,高效處理支付事務。
  3. 在與 Java 管理系統對接時,透過合適的通訊協議(如 RESTful API)實現資料的準確互動。確保 Java 系統能夠方便地發起支付請求,並及時獲取支付結果,實現跨語言專案的協同工作。

(三)高效能與高可靠性

支付中臺需要在高併發的支付請求環境下保持高效能執行,確保快速響應。同時,必須保證支付過程的可靠性,避免出現支付失敗、資料丟失或不一致等問題,切實保障使用者和商家的利益。

四、設計模式的選擇

(一)工廠模式

在處理不同支付平臺的支付請求時,工廠模式發揮著關鍵作用。以下是一個簡單的工廠模式示例程式碼:

// PaymentProcessor 是支付處理器介面
type PaymentProcessor interface {
    ProcessPayment(paymentRequest PaymentRequest) error
}

// PaymentRequest 包含支付請求的資訊
type PaymentRequest struct {
    Amount   float64
    Platform string
    // 其他支付相關資訊
}

// WeChatPaymentProcessor 微信支付處理器結構體
type WeChatPaymentProcessor struct{}

// ProcessPayment 實現微信支付處理邏輯
func (wp *WeChatPaymentProcessor) ProcessPayment(paymentRequest PaymentRequest) error {
    // 微信支付處理邏輯,如呼叫微信支付 API 等
    return nil
}

// AlipayPaymentProcessor 支付寶支付處理器結構體
type AlipayPaymentProcessor struct{}

// ProcessPayment 實現支付寶支付處理邏輯
func (ap *AlipayPaymentProcessor) ProcessPayment(paymentRequest PaymentRequest) error {
    // 支付寶支付處理邏輯,如呼叫支付寶支付 API 等
    return nil
}

// PaymentProcessorFactory 支付處理器工廠結構體
type PaymentProcessorFactory struct{}

// CreatePaymentProcessor 根據支付平臺型別建立相應的支付處理器
func (f *PaymentProcessorFactory) CreatePaymentProcessor(platform string) PaymentProcessor {
    switch platform {
    case "wechat":
        return &WeChatPaymentProcessor{}
    case "alipay":
        return &AlipayPaymentProcessor{}
    default:
        return nil
    }
}

透過這種方式,根據支付平臺型別(如 “wechat” 或 “alipay”)建立相應的支付處理器物件,將不同支付平臺的處理邏輯解耦,便於後續的擴充套件和維護。當需要新增新的支付平臺時,只需在工廠類的CreatePaymentProcessor方法中新增相應的建立邏輯即可。

(二)策略模式

對於不同的支付策略(如掃碼支付、APP 內支付等),策略模式是一種理想的選擇。

以下是一個簡單的策略模式示例:

// PaymentStrategy 支付策略介面
type PaymentStrategy interface {
    Pay(paymentInfo PaymentInfo) error
}

// ScanCodePayment 掃碼支付策略結構體
type ScanCodePayment struct{}

// Pay 實現掃碼支付邏輯
func (s *ScanCodePayment) Pay(paymentInfo PaymentInfo) error {
    // 掃碼支付處理邏輯
    return nil
}

// InAppPayment APP 內支付策略結構體
type InAppPayment struct{}

// Pay 實現 APP 內支付邏輯
func (i *InAppPayment) Pay(paymentInfo PaymentInfo) error {
    // APP 內支付處理邏輯
    return nil
}

// PaymentContext 支付上下文結構體,用於執行支付策略
type PaymentContext struct {
    strategy PaymentStrategy
}

// SetStrategy 設定支付策略
func (pc *PaymentContext) SetStrategy(strategy PaymentStrategy) {
    pc.strategy = strategy
}

// ExecutePayment 執行支付操作
func (pc *PaymentContext) ExecutePayment(paymentInfo PaymentInfo) error {
    return pc.strategy.Pay(paymentInfo)
}

每個支付策略都實現PaymentStrategy介面,在執行時根據使用者選擇或業務邏輯選擇合適的支付策略。這種設計模式使得支付中臺能夠靈活應對各種支付場景,並且方便對每個支付策略進行單獨的測試和最佳化。

(三)外觀模式

在對接不同型別的專案(Go 應用、遊戲專案、Java 管理系統等)時,外觀模式可建立一個統一的介面層。以下是一個簡單的外觀模式示例:

// PaymentFacade 支付外觀結構體
type PaymentFacade struct {
    paymentProcessorFactory *PaymentProcessorFactory
}

// NewPaymentFacade 建立支付外觀例項
func NewPaymentFacade() *PaymentFacade {
    return &PaymentFacade{
        paymentProcessorFactory: &PaymentProcessorFactory{},
    }
}

// ProcessPayment 透過外觀模式處理支付請求
func (pf *PaymentFacade) ProcessPayment(paymentRequest PaymentRequest) error {
    processor := pf.paymentProcessorFactory.CreatePaymentProcessor(paymentRequest.Platform)
    if processor == nil {
        return fmt.Errorf("unsupported payment platform")
    }
    return processor.ProcessPayment(paymentRequest)
}

這個介面層隱藏了支付中臺內部複雜的實現細節,對外提供簡單、一致的介面。不同型別的專案只需與這個外觀介面進行互動,大大降低了專案整合支付功能的難度。

五、開發框架的選擇

(一)Go 的 Web 框架:Gin (最主流的框架)

Gin 是一款輕量級且高效能的 Go Web 框架,非常適合構建支付中臺的 API 服務。其核心優勢在於快速的路由功能和強大的中介軟體支援。

以下是使用 Gin 構建支付中臺 API 的基本示例程式碼:

package main

import (
    "net/http"

    "github.com/gin-gonic/gin"
)

func main() {
    router := gin.Default()

    // 定義支付請求處理路由
    router.POST("/payment", func(c *gin.Context) {
        var paymentRequest PaymentRequest
        if err := c.ShouldBindJSON(&paymentRequest); err!= nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }
        // 使用支付外觀處理支付請求
        paymentFacade := NewPaymentFacade()
        if err := paymentFacade.ProcessPayment(paymentRequest); err!= nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
            return
        }
        c.JSON(http.StatusOK, gin.H{"message": "Payment processed successfully"})
    }

    router.Run(":8080")
}

咱們仍然使用目前閃送專案的running-server-pro的框架做開發,區別是新啟動一個支付中臺的專案。

在支付中臺開發中,利用 Gin 來處理來自不同專案的支付請求。它可以接收 HTTP 請求、解析請求引數,然後呼叫相應的支付處理邏輯,並返回結果。透過中介軟體,可以輕鬆實現一些通用的功能,如身份驗證、請求日誌記錄、錯誤處理等。

(二)資料庫框架:GORM

支付中臺需要妥善儲存支付相關的資料,如訂單資訊、支付記錄等。GORM 作為一個功能強大的 Go 語言 ORM 框架,支援多種資料庫(如 MySQL、PostgreSQL 等),為資料庫操作提供了極大的便利。

以下是使用 GORM 建立訂單模型和進行基本資料庫操作的示例程式碼:

package main

import (
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

// Order 訂單結構體
type Order struct {
    gorm.Model
    OrderNumber string  `gorm:"unique"`
    Amount      float64
    Status      string
    // 其他訂單相關欄位
}

func main() {
    // 連線資料庫
    db, err := gorm.Open(mysql.Open("user:password@tcp(127.0.0.1:3306)/payment_db?charset=utf8mb4&parseTime=True&loc=Local"), &gorm.Config{})
    if err!= nil {
        panic("failed to connect database")
    }

    // 自動遷移訂單表
    db.AutoMigrate(&Order{})

    // 建立新訂單示例
    newOrder := Order{
        OrderNumber: "20241117001",
        Amount:      100.0,
        Status:      "pending",
    }
    db.Create(&newOrder)

    // 查詢訂單示例
    var order Order
    db.Where("order_number =?", "20241117001").First(&order)
}

使用 GORM 可以方便地進行資料庫操作,如建立表、插入資料、查詢資料等。同時,GORM 還提供了模型關聯、事務處理等高階功能,有助於保證支付資料的完整性和一致性。

六、支付中臺的核心功能模組

(一)支付請求處理模組

  1. 接收來自不同專案的支付請求,透過 Gin 框架解析請求中的支付資訊,包括支付金額、支付平臺、支付方式、訂單編號等。
  2. 根據支付平臺型別,利用工廠模式建立相應的支付處理器,然後呼叫支付處理器的支付方法,發起支付請求。在這個過程中,要處理可能出現的各種錯誤,如網路問題、支付平臺介面返回錯誤等,並將錯誤資訊以合適的格式反饋給發起支付請求的專案。
  3. 以下是支付請求處理模組的簡化程式碼示例:
func handlePaymentRequest(c *gin.Context) {
    var paymentRequest PaymentRequest
    if err := c.ShouldBindJSON(&paymentRequest); err!= nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    paymentFacade := NewPaymentFacade()
    if err := paymentFacade.ProcessPayment(paymentRequest); err!= nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }
    c.JSON(http.StatusOK, gin.H{"message": "Payment processed successfully"})
}

(二)支付結果回撥處理模組

  1. 接收支付平臺的支付結果回撥,首先驗證回撥的合法性,如進行簽名驗證等操作。
  2. 根據回撥結果更新支付狀態,例如將訂單狀態從 “支付中” 更新為 “已支付” 或 “支付失敗”。同時,透過合適的方式(如訊息佇列、HTTP 通知等)通知相關專案支付結果,以便它們進行後續的業務處理,如發貨、更新使用者賬戶餘額等。
  3. 以下是支付結果回撥處理模組的部分程式碼示例:
func handlePaymentCallback(c *gin.Context) {
    var callbackData PaymentCallbackData
    if err := c.ShouldBindJSON(&callbackData); err!= nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    if!verifyCallbackSignature(callbackData) {
        c.JSON(http.StatusForbidden, gin.H{"error": "Invalid callback signature"})
        return
    }
    // 根據回撥資料更新訂單狀態
    updateOrderStatus(callbackData)
    // 通知相關專案支付結果
    notifyRelatedProjects(callbackData)
    c.JSON(http.StatusOK, gin.H{"message": "Callback processed successfully"})
}

(三)訂單管理模組

  1. 負責訂單的建立、查詢、更新和刪除等操作。當接收到支付請求時,建立相應的訂單記錄,並將訂單資訊儲存到資料庫中。可以使用 GORM 框架來實現訂單資料的持久化操作。
  2. 提供訂單查詢介面,方便其他專案查詢訂單狀態。同時,在支付結果回撥處理時,根據支付結果更新訂單狀態。以下是訂單建立和查詢的示例程式碼:
func createOrder(paymentRequest PaymentRequest) (*Order, error) {
    newOrder := Order{
        OrderNumber: generateOrderNumber(),
        Amount:      paymentRequest.Amount,
        Status:      "pending",
    }
    db.Create(&newOrder)
    return &newOrder, nil
}

func getOrderByNumber(orderNumber string) (*Order, error) {
    var order Order
    db.Where("order_number =?", orderNumber).First(&order)
    return &order, nil
}

(四)配置管理模組

  1. 儲存支付中臺的配置資訊,如各個支付平臺的 API 金鑰、回撥地址、支付引數等。可以將配置資訊儲存在配置檔案(如 JSON 格式或 YAML 格式)中,或者使用環境變數來儲存敏感資訊。
  2. 提供配置的讀取和更新功能,以便在需要時修改支付平臺相關的配置,同時保證配置資訊的安全性。對於敏感資訊,如 API 金鑰,要進行加密儲存和安全傳輸。以下是讀取配置檔案的示例程式碼(假設使用 JSON 格式配置檔案):
type PaymentConfig struct {
    WeChatConfig WeChatConfig `json:"wechat"`
    AlipayConfig AlipayConfig `json:"alipay"`
    // 其他配置項
}

type WeChatConfig struct {
    AppID     string `json:"app_id"`
    APIKey    string `json:"api_key"`
    Callback  string `json:"callback"`
    // 其他微信支付相關配置
}

type AlipayConfig struct {
    AppID     string `json:"app_id"`
    APIKey    string `json:"api_key"`
    Callback  string `json:"callback"`
    // 其他支付寶支付相關配置
}

func readConfig() (*PaymentConfig, error) {
    data, err := ioutil.ReadFile("payment_config.json")
    if err!= nil {
        return nil, err
    }
    var config PaymentConfig
    if err := json.Unmarshal(data, &config); err!= nil {
        return nil, err
    }
    return &config, nil
}

七、與不同專案的對接實現

(一)與 Go 應用專案對接

在 Go 應用專案中,透過引入支付中臺的 Go 包,使用 Go 語言的依賴管理工具(如 Go Modules)來管理依賴。以下是對接示例:

package main

import (
    "fmt"
    "your_payment_middleware_package"
)

func main() {
    paymentRequest := your_payment_middleware_package.PaymentRequest{
        Amount:   50.0,
        Platform: "wechat",
        // 其他支付資訊,如訂單號等
        OrderID: "202411170001", 
    }
    paymentFacade := your_payment_middleware_package.NewPaymentFacade()
    err := paymentFacade.ProcessPayment(paymentRequest)
    if err!= nil {
        fmt.Printf("支付失敗: %v\n", err)
        return
    }
    fmt.Println("支付成功")
}

(二)與遊戲專案對接

  1. 遊戲內支付流程 對於遊戲內支付,遊戲伺服器充當與支付中臺互動的橋樑。遊戲客戶端發起支付請求後,遊戲伺服器收集支付相關資訊,如玩家 ID、支付金額、購買的虛擬物品資訊等,並構建支付請求傳送給支付中臺。
  2. 程式碼示例 以下是遊戲伺服器處理支付請求的簡化程式碼:
package main

import (
    "your_payment_middleware_package"
    "log"
)

func handleGamePayment(playerID string, itemID string, amount float64) {
    paymentRequest := your_payment_middleware_package.PaymentRequest{
        Amount:   amount,
        Platform: "wechat", // 假設遊戲主要使用微信支付,可根據玩家選擇調整
        OrderID:  generateOrderID(playerID, itemID), // 根據玩家和物品生成唯一訂單號
        // 其他遊戲相關支付資訊,如遊戲內訂單描述等
        Description: fmt.Sprintf("購買遊戲物品 %s", itemID), 
    }
    paymentFacade := your_payment_middleware_package.NewPaymentFacade()
    err := paymentFacade.ProcessPayment(paymentRequest)
    if err!= nil {
        log.Printf("玩家 %s 支付失敗: %v", playerID, err)
        // 通知遊戲客戶端支付失敗,可回滾遊戲內相關操作,如不扣除虛擬貨幣
        notifyGameClient(playerID, false) 
        return
    }
    // 支付成功,更新遊戲內玩家資料,如增加虛擬物品、扣除虛擬貨幣
    updatePlayerData(playerID, itemID) 
    notifyGameClient(playerID, true)
}

(三)與 Java 管理系統對接

  1. 透過 RESTful API 實現對接 支付中臺提供一組 RESTful 介面,Java 管理系統透過 HTTP 請求呼叫這些介面來發起支付請求和獲取支付結果。在 Java 端,可以使用 Java 的 HTTP 客戶端庫(如 Apache HttpClient 或 OkHttp 等)來傳送請求,並處理支付中臺返回的 JSON 格式的結果資料。
  2. 程式碼示例(使用 Apache HttpClient)
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import com.google.gson.Gson;

public class PaymentClient {
    public static void main(String[] args) {
        try {
            CloseableHttpClient httpClient = HttpClients.createDefault();
            HttpPost httpPost = new HttpPost("http://your_payment_middleware_server/payment");
            PaymentRequest paymentRequest = new PaymentRequest();
            paymentRequest.setAmount(100.0);
            paymentRequest.setPlatform("alipay");
            paymentRequest.setOrderID("202411170002");
            Gson gson = new Gson();
            String json = gson.toJson(paymentRequest);
            StringEntity entity = new StringEntity(json);
            httpPost.setEntity(entity);
            httpPost.setHeader("Content-Type", "application/json");

            CloseableHttpResponse response = httpClient.execute(httpPost);
            HttpEntity responseEntity = response.getEntity();
            if (response.getStatusLine().getStatusCode() == 200) {
                String result = EntityUtils.toString(responseEntity);
                PaymentResponse paymentResponse = gson.fromJson(result, PaymentResponse.class);
                if (paymentResponse.isSuccess()) {
                    System.out.println("支付成功");
                } else {
                    System.out.println("支付失敗: " + paymentResponse.getErrorMessage());
                }
            } else {
                System.out.println("請求支付中臺失敗");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class PaymentRequest {
    private double amount;
    private String platform;
    private String orderID;
    // 生成對應的getter和setter方法
    public double getAmount() {
        return amount;
    }
    public void setAmount(double amount) {
        this.amount = amount;
    }
    public String getPlatform() {
        return platform;
    }
    public void setPlatform(String platform) {
        this.platform = platform;
    }
    public String getOrderID() {
        return orderID;
    }
    public void setOrderID(String orderID) {
        this.orderID = orderID;
    }
}

class PaymentResponse {
    private boolean success;
    private String errorMessage;
    // 生成對應的getter和setter方法
    public boolean isSuccess() {
        return success;
    }
    public void setSuccess(boolean success) {
        this.success = success;
    }
    public String getErrorMessage() {
        return errorMessage;
    }
    public void setErrorMessage(String errorMessage) {
        this.errorMessage = errorMessage;
    }
}

八、安全與效能最佳化

(一)安全措施

  1. 資料傳輸加密 在支付請求和結果回撥過程中,使用 SSL/TLS 協議對資料進行加密傳輸,防止資料在網路傳輸過程中被竊取或篡改。對於支付中臺與支付平臺之間的互動,確保使用符合安全標準的加密方式。
  2. API 金鑰管理 對各個支付平臺的 API 金鑰進行嚴格的安全儲存。可以將金鑰儲存在加密的配置檔案中,或者使用專門的金鑰管理系統。在執行時,僅在需要時解密和使用金鑰,並且限制對金鑰儲存區域的訪問許可權,防止內部洩露。
  3. 輸入驗證和安全防護 在支付中臺內部,對所有接收的輸入資料進行嚴格驗證,防止惡意攻擊,如 SQL 注入、跨站指令碼攻擊(XSS)等。對於使用者輸入的支付金額、訂單號等資訊,進行格式和範圍檢查。同時,使用安全的編碼實踐,如對輸出資料進行編碼,避免在前端顯示時出現安全漏洞。

(二)效能最佳化

  1. 併發處理最佳化 利用 Go 語言的高併發特性,對支付請求進行併發處理。例如,使用goroutinechannel機制來實現非同步處理支付請求,提高系統的吞吐量。但在併發處理過程中,要注意資源的合理利用和併發安全問題,如使用互斥鎖來保護共享資源,避免資料競爭。
  2. 資料庫效能最佳化 對支付相關資料的儲存操作進行最佳化。合理設計資料庫表結構,根據查詢和業務邏輯建立合適的索引。例如,對訂單表的訂單號、支付狀態等經常查詢的欄位建立索引,提高查詢效率。同時,可以採用快取機制來快取一些常用的資料,如支付平臺的配置資訊、頻繁查詢的訂單狀態等,減少資料庫的查詢壓力,提高系統的響應速度。
  3. 效能測試與監控 定期對支付中臺進行效能測試,模擬高併發的支付場景,使用效能測試工具(如 Gatling 等)來檢測系統的響應時間、吞吐量等效能指標。同時,建立完善的監控系統,實時監控支付中臺的執行狀態,包括伺服器資源使用情況、支付請求處理情況等,及時發現效能瓶頸並進行最佳化。

九、結論

透過合理選擇設計模式和開發框架,開發一個基於 Go 語言的支付中臺,可以有效地支援微信、支付寶等主流支付平臺,並能方便地與多個型別的專案進行對接。在開發過程中,注重安全和效能的最佳化是至關重要的,這可以滿足商業環境中對支付系統的高要求,為使用者和商家提供穩定、高效、安全的支付服務,推動企業數字化業務的順利開展。同時,支付中臺的架構應具有一定的靈活性和可擴充套件性,以適應未來支付業務的發展和變化。

十、交流討論

大家有什麼問題和建議,請直接評論留言這篇方案,我會繼續最佳化。

集思廣益,一起進步!

歡迎關注 ❤

我們搞了一個免費的面試真題共享群,互通有無,一起刷題進步。

沒準能讓你能刷到自己意向公司的最新面試題呢。

感興趣的朋友們可以加我微信:wangzhongyang1993,備註:sf面試群。

相關文章