179 lines
6.8 KiB
Go
179 lines
6.8 KiB
Go
package main
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"io"
|
||
"net/http"
|
||
"net/url"
|
||
"strings"
|
||
|
||
"github.com/awa/go-iap/appstore/api"
|
||
"github.com/labstack/echo/v4"
|
||
"go.uber.org/zap"
|
||
)
|
||
|
||
// 处理appstore的回调
|
||
func IapCallbackHandler(c echo.Context) error {
|
||
// 获取请求体
|
||
body, err := io.ReadAll(c.Request().Body) // {"signedPayload":"..."}
|
||
if err != nil {
|
||
logger.Error("Failed to read request body", zap.Error(err))
|
||
return echo.NewHTTPError(http.StatusBadRequest, "read body error.")
|
||
//return c.JSON(http.StatusInternalServerError, "Failed to read request body")
|
||
}
|
||
|
||
// App Store Server Notification Request JSON String
|
||
var request AppStoreServerRequest
|
||
err2 := json.Unmarshal([]byte(body), &request) // bind byte to header structure
|
||
if err2 != nil {
|
||
logger.Error("AppStoreServerRequest Unmarshal error.", zap.Error(err))
|
||
return echo.NewHTTPError(http.StatusBadRequest, "Failed to read request body")
|
||
}
|
||
|
||
// Apple Root CA - G3 Root certificate
|
||
// for details: https://www.apple.com/certificateauthority/
|
||
// you need download it and covert it to a valid pem file in order to verify X5c certificates
|
||
// `openssl x509 -in AppleRootCA-G3.cer -out cert.pem`
|
||
appStoreServerNotification, err := IAP_Notify_New(request.SignedPayload, IAP_ROOT_CERT)
|
||
if err != nil {
|
||
logger.Error("IAP_Notify_New error.", zap.Error(err))
|
||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to decode body")
|
||
}
|
||
|
||
// 打印字段,为了显示方便,把加密串替换掉
|
||
appStoreServerNotification.Payload.Data.SignedRenewalInfo = "..."
|
||
appStoreServerNotification.Payload.Data.SignedTransactionInfo = "..."
|
||
logger.Debug("appStoreServerNotification", zap.Any("appStoreServerNotification", appStoreServerNotification))
|
||
|
||
// 打印日志
|
||
//buff, _ := json.Marshal(&appStoreServerNotification)
|
||
//fmt.Println(string(buff))
|
||
UpdateOrderByNotify(appStoreServerNotification)
|
||
|
||
setResponse(c, nil)
|
||
return nil
|
||
}
|
||
|
||
// 处理从客户端过来的订单验证请求
|
||
func IapVerify(c echo.Context) error {
|
||
var request struct {
|
||
TransID string `json:"transid" form:"transid"`
|
||
AppAccountToken string `json:"appaccounttoken" form:"appaccounttoken"`
|
||
Env string `json:"env" form:"env"`
|
||
ProductID string `json:"productid" form:"productid"`
|
||
ReceiptData string `json:"receiptdata" form:"receiptdata"`
|
||
}
|
||
|
||
if err := c.Bind(&request); err != nil {
|
||
logger.Error("read param error.", zap.Error(err))
|
||
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
||
}
|
||
|
||
// 验证 receiptdata 是否为有效的 JSON 字符串
|
||
var jsonObj interface{}
|
||
if err := json.Unmarshal([]byte(request.ReceiptData), &jsonObj); err != nil {
|
||
logger.Debug("receiptdata from request", zap.Any("receiptdata", request.ReceiptData)) // 打印日志
|
||
} else {
|
||
logger.Debug("receiptdata from request", zap.Any("receiptdata", jsonObj)) // 打印日志
|
||
}
|
||
|
||
var isSandBox = true
|
||
// 忽略大小写进行比较
|
||
if strings.EqualFold(request.Env, "Production") {
|
||
isSandBox = false
|
||
}
|
||
|
||
cfg := &api.StoreConfig{
|
||
KeyContent: []byte(IAP_ACCOUNT_KEY), // Loads a .p8 certificate
|
||
KeyID: IAP_KEY_ID, // Your private key ID from App Store Connect (Ex: 2X9R4HXF34)
|
||
BundleID: IAP_BUNDLEID, // Your app’s bundle ID
|
||
Issuer: IAP_ISSUER, // Your issuer ID from the Keys page in App Store Connect (Ex: "57246542-96fe-1a63-e053-0824d011072a")
|
||
Sandbox: isSandBox, // default is Production
|
||
}
|
||
client := api.NewStoreClient(cfg)
|
||
ctx := context.Background()
|
||
response, err := client.GetTransactionInfo(ctx, request.TransID)
|
||
if err != nil {
|
||
logger.Error("GetTransactionInfo error.", zap.Error(err))
|
||
return echo.NewHTTPError(http.StatusInternalServerError, "GetTransactionInfo error")
|
||
}
|
||
|
||
transantion, err := client.ParseSignedTransaction(response.SignedTransactionInfo)
|
||
if err != nil {
|
||
logger.Error("ParseSignedTransaction error.", zap.Error(err))
|
||
return echo.NewHTTPError(http.StatusInternalServerError, "ParseSignedTransaction error")
|
||
}
|
||
|
||
logger.Debug("transantion", zap.Any("transantion", transantion)) // 打印日志
|
||
//buff, _ := json.Marshal(&transantion)
|
||
//fmt.Println(string(buff))
|
||
|
||
if transantion.TransactionID != request.TransID {
|
||
logger.Error("transactionId not match.", zap.Any("transantion.TransactionID", transantion.TransactionID), zap.Any("request.TransID", request.TransID))
|
||
return echo.NewHTTPError(http.StatusInternalServerError, "transactionId not match")
|
||
}
|
||
|
||
// 写入DB
|
||
GID, _ := c.Get(KEY_GID).(int)
|
||
errDB := UpdateOrderByVerify(GID, request.AppAccountToken, transantion.OriginalTransactionId, transantion)
|
||
if errDB != nil {
|
||
logger.Error("UpdateOrderByVerify error.", zap.Error(errDB))
|
||
return echo.NewHTTPError(http.StatusInternalServerError, "UpdateOrderByVerify error")
|
||
}
|
||
|
||
setResponse(c, map[string]string{"ret": "ok"})
|
||
return nil
|
||
}
|
||
|
||
// 查询订单历史信息,通常是内部服务发起
|
||
func IapHistory(c echo.Context) error {
|
||
var request struct {
|
||
OriginTransID string `json:"origintransid" form:"origintransid"`
|
||
Lang string `json:"lang" form:"lang"`
|
||
}
|
||
|
||
if err := c.Bind(&request); err != nil {
|
||
logger.Error("read param error.", zap.Error(err))
|
||
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
||
}
|
||
|
||
cfg := &api.StoreConfig{
|
||
KeyContent: []byte(IAP_ACCOUNT_KEY), // Loads a .p8 certificate
|
||
KeyID: IAP_KEY_ID, // Your private key ID from App Store Connect (Ex: 2X9R4HXF34)
|
||
BundleID: IAP_BUNDLEID, // Your app’s bundle ID
|
||
Issuer: IAP_ISSUER, // Your issuer ID from the Keys page in App Store Connect (Ex: "57246542-96fe-1a63-e053-0824d011072a")
|
||
Sandbox: true, // default is Production
|
||
}
|
||
client := api.NewStoreClient(cfg)
|
||
|
||
query := &url.Values{}
|
||
query.Set("productType", "AUTO_RENEWABLE")
|
||
//query.Set("productType", "NON_CONSUMABLE")
|
||
ctx := context.Background()
|
||
responses, err := client.GetTransactionHistory(ctx, request.OriginTransID, query)
|
||
if err != nil {
|
||
logger.Error("GetTransactionHistory error.", zap.Error(err))
|
||
return echo.NewHTTPError(http.StatusInternalServerError, "GetTransactionHistory error")
|
||
}
|
||
|
||
// 由于接口字段中有HasMore,所以 responses 是个数组,每个 responses 中的 transantions 也是数组
|
||
var allTransactions []*api.JWSTransaction
|
||
for _, response := range responses {
|
||
transantions, err := client.ParseSignedTransactions(response.SignedTransactions)
|
||
if err != nil {
|
||
logger.Error("ParseSignedTransactions error.", zap.Error(err))
|
||
return echo.NewHTTPError(http.StatusInternalServerError, "ParseSignedTransactions error")
|
||
}
|
||
allTransactions = append(allTransactions, transantions...)
|
||
|
||
logger.Debug("transantions", zap.Any("transantions", transantions)) // 打印
|
||
//buff, _ := json.Marshal(&transantions)
|
||
//fmt.Println(string(buff))
|
||
}
|
||
|
||
setResponse(c, allTransactions)
|
||
//setResponse(c, map[string]string{"ret": "ok"})
|
||
return nil
|
||
}
|