ca 服务升级
This commit is contained in:
parent
ff7959deec
commit
eb11666b46
1
.gitignore
vendored
1
.gitignore
vendored
@ -19,3 +19,4 @@
|
||||
.git/
|
||||
.DS_Store/
|
||||
data/
|
||||
docs/
|
||||
|
||||
141
api/controller/caV2.go
Normal file
141
api/controller/caV2.go
Normal file
@ -0,0 +1,141 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"hospital-admin-api/api/requests"
|
||||
"hospital-admin-api/api/responses"
|
||||
cav2 "hospital-admin-api/extend/ca_v2"
|
||||
"hospital-admin-api/global"
|
||||
"hospital-admin-api/utils"
|
||||
)
|
||||
|
||||
type CaV2 struct{}
|
||||
|
||||
// GetServiceURL 获取云证书生命周期管理界面链接地址
|
||||
func (r *CaV2) GetServiceURL(c *gin.Context) {
|
||||
caRequest := requests.CaV2Request{}
|
||||
req := caRequest.GetServiceURL
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
responses.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if err := global.Validate.Struct(req); err != nil {
|
||||
responses.FailWithMessage(utils.Translate(err), c)
|
||||
return
|
||||
}
|
||||
|
||||
if msg := req.ValidateBusinessFields(); msg != "" {
|
||||
responses.FailWithMessage(msg, c)
|
||||
return
|
||||
}
|
||||
|
||||
client := cav2.NewClientFromConfig()
|
||||
result, err := client.GetServiceURL(cav2.GetServiceURLRequest{
|
||||
CertType: req.CertType,
|
||||
Type: req.Type,
|
||||
EntityID: req.EntityID,
|
||||
UserName: req.UserName,
|
||||
UserIDCard: req.UserIDCard,
|
||||
UserPhone: req.UserPhone,
|
||||
Email: req.Email,
|
||||
EnterpriseName: req.EnterpriseName,
|
||||
SocialCreditCode: req.SocialCreditCode,
|
||||
DeptPerson: req.DeptPerson,
|
||||
DeptPersonCode: req.DeptPersonCode,
|
||||
DeptPersonPhone: req.DeptPersonPhone,
|
||||
AgentName: req.AgentName,
|
||||
AgentIDCard: req.AgentIDCard,
|
||||
AuthMethod: req.AuthMethod,
|
||||
})
|
||||
if err != nil {
|
||||
responses.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
responses.OkWithData(result, c)
|
||||
}
|
||||
|
||||
// CertSign 云证书签名
|
||||
func (r *CaV2) CertSign(c *gin.Context) {
|
||||
caRequest := requests.CaV2Request{}
|
||||
req := caRequest.CertSign
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
responses.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if err := global.Validate.Struct(req); err != nil {
|
||||
responses.FailWithMessage(utils.Translate(err), c)
|
||||
return
|
||||
}
|
||||
|
||||
client := cav2.NewClientFromConfig()
|
||||
result, err := client.CertSign(cav2.CertSignRequest{
|
||||
EntityID: req.EntityID,
|
||||
RequestID: req.RequestID,
|
||||
BusinessType: req.BusinessType,
|
||||
ToSign: req.ToSign,
|
||||
NotifyURL: req.NotifyURL,
|
||||
JumpURL: req.JumpURL,
|
||||
ExtParam: req.ExtParam,
|
||||
})
|
||||
if err != nil {
|
||||
responses.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
responses.OkWithData(result, c)
|
||||
}
|
||||
|
||||
// GetCertSignResult 获取云证书签名结果
|
||||
func (r *CaV2) GetCertSignResult(c *gin.Context) {
|
||||
caRequest := requests.CaV2Request{}
|
||||
req := caRequest.GetCertSignResult
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
responses.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if err := global.Validate.Struct(req); err != nil {
|
||||
responses.FailWithMessage(utils.Translate(err), c)
|
||||
return
|
||||
}
|
||||
|
||||
client := cav2.NewClientFromConfig()
|
||||
result, err := client.GetCertSignResult(cav2.GetCertSignResultRequest{
|
||||
RequestID: req.RequestID,
|
||||
})
|
||||
if err != nil {
|
||||
responses.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
responses.OkWithData(result, c)
|
||||
}
|
||||
|
||||
// PasswordLessSignInfo 云证书免密签署状态查询
|
||||
func (r *CaV2) PasswordLessSignInfo(c *gin.Context) {
|
||||
caRequest := requests.CaV2Request{}
|
||||
req := caRequest.PasswordLessSignInfo
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
responses.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if err := global.Validate.Struct(req); err != nil {
|
||||
responses.FailWithMessage(utils.Translate(err), c)
|
||||
return
|
||||
}
|
||||
|
||||
client := cav2.NewClientFromConfig()
|
||||
result, err := client.PasswordLessSignInfo(cav2.PasswordLessSignInfoRequest{
|
||||
EntityID: req.EntityID,
|
||||
})
|
||||
if err != nil {
|
||||
responses.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
responses.OkWithData(result, c)
|
||||
}
|
||||
78
api/requests/caV2.go
Normal file
78
api/requests/caV2.go
Normal file
@ -0,0 +1,78 @@
|
||||
package requests
|
||||
|
||||
import "strings"
|
||||
|
||||
type CaV2Request struct {
|
||||
GetServiceURL GetCaV2ServiceURL
|
||||
CertSign GetCaV2CertSign
|
||||
GetCertSignResult GetCaV2CertSignResult
|
||||
PasswordLessSignInfo GetCaV2PasswordLessSignInfo
|
||||
}
|
||||
|
||||
type GetCaV2ServiceURL struct {
|
||||
CertType string `json:"certType" form:"certType" validate:"required,oneof=Personal Organizational OrgPerson" label:"证书类型"`
|
||||
Type string `json:"type" form:"type" validate:"required,oneof=BUSINESS_CENTER CERT_APPLY CERT_RENEW CERT_UPDATE CERT_REVOKE" label:"业务类型"`
|
||||
EntityID string `json:"entityId" form:"entityId" validate:"required" label:"用户唯一标识"`
|
||||
UserName string `json:"userName" form:"userName"`
|
||||
UserIDCard string `json:"userIdCard" form:"userIdCard"`
|
||||
UserPhone string `json:"userPhone" form:"userPhone"`
|
||||
Email string `json:"email" form:"email"`
|
||||
EnterpriseName string `json:"enterpriseName" form:"enterpriseName"`
|
||||
SocialCreditCode string `json:"socialCreditCode" form:"socialCreditCode"`
|
||||
DeptPerson string `json:"deptPerson" form:"deptPerson"`
|
||||
DeptPersonCode string `json:"deptPersonCode" form:"deptPersonCode"`
|
||||
DeptPersonPhone string `json:"deptPersonPhone" form:"deptPersonPhone"`
|
||||
AgentName string `json:"agentName" form:"agentName"`
|
||||
AgentIDCard string `json:"agentIdCard" form:"agentIdCard"`
|
||||
AuthMethod string `json:"authMethod" form:"authMethod" validate:"omitempty,oneof=face SMS" label:"认证方式"`
|
||||
}
|
||||
|
||||
func (r GetCaV2ServiceURL) ValidateBusinessFields() string {
|
||||
switch r.CertType {
|
||||
case "Personal":
|
||||
if strings.TrimSpace(r.UserName) == "" || strings.TrimSpace(r.UserIDCard) == "" || strings.TrimSpace(r.UserPhone) == "" {
|
||||
return "个人证书必须填写 userName、userIdCard、userPhone"
|
||||
}
|
||||
case "Organizational":
|
||||
if strings.TrimSpace(r.EnterpriseName) == "" ||
|
||||
strings.TrimSpace(r.SocialCreditCode) == "" ||
|
||||
strings.TrimSpace(r.DeptPerson) == "" ||
|
||||
strings.TrimSpace(r.DeptPersonCode) == "" ||
|
||||
strings.TrimSpace(r.DeptPersonPhone) == "" {
|
||||
return "企业证书必须填写 enterpriseName、socialCreditCode、deptPerson、deptPersonCode、deptPersonPhone"
|
||||
}
|
||||
case "OrgPerson":
|
||||
if strings.TrimSpace(r.UserName) == "" ||
|
||||
strings.TrimSpace(r.UserIDCard) == "" ||
|
||||
strings.TrimSpace(r.UserPhone) == "" {
|
||||
return "企业个人证书必须填写 userName、userIdCard、userPhone"
|
||||
}
|
||||
if strings.TrimSpace(r.EnterpriseName) == "" ||
|
||||
strings.TrimSpace(r.SocialCreditCode) == "" ||
|
||||
strings.TrimSpace(r.DeptPerson) == "" ||
|
||||
strings.TrimSpace(r.DeptPersonCode) == "" ||
|
||||
strings.TrimSpace(r.DeptPersonPhone) == "" {
|
||||
return "企业个人证书必须填写 enterpriseName、socialCreditCode、deptPerson、deptPersonCode、deptPersonPhone"
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
type GetCaV2CertSign struct {
|
||||
EntityID string `json:"entityId" form:"entityId" validate:"required" label:"用户唯一标识"`
|
||||
RequestID string `json:"requestId" form:"requestId" validate:"required" label:"业务流水号"`
|
||||
BusinessType string `json:"businessType" form:"businessType" validate:"required,oneof=businessCenter certApply certRenewcertRenew certUpdate certRevoke certInfo certSign certUpdatePin" label:"业务类型"`
|
||||
ToSign string `json:"toSign" form:"toSign" validate:"required" label:"签名原文"`
|
||||
NotifyURL string `json:"notifyUrl" form:"notifyUrl"`
|
||||
JumpURL string `json:"jumpUrl" form:"jumpUrl"`
|
||||
ExtParam string `json:"extParam" form:"extParam"`
|
||||
}
|
||||
|
||||
type GetCaV2CertSignResult struct {
|
||||
RequestID string `json:"requestId" form:"requestId" validate:"required" label:"业务流水号"`
|
||||
}
|
||||
|
||||
type GetCaV2PasswordLessSignInfo struct {
|
||||
EntityID string `json:"entityId" form:"entityId" validate:"required" label:"用户唯一标识"`
|
||||
}
|
||||
@ -98,6 +98,15 @@ func publicRouter(r *gin.Engine, api controller.Api) {
|
||||
}
|
||||
}
|
||||
|
||||
caV2 := controller.CaV2{}
|
||||
caV2Group := r.Group("/open/ca/v2")
|
||||
{
|
||||
caV2Group.POST("/service-url", caV2.GetServiceURL)
|
||||
caV2Group.POST("/cert-sign", caV2.CertSign)
|
||||
caV2Group.POST("/cert-sign/result", caV2.GetCertSignResult)
|
||||
caV2Group.POST("/passwordless-sign-info", caV2.PasswordLessSignInfo)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// adminRouter 公共路由-验证权限
|
||||
|
||||
@ -53,6 +53,11 @@ ca-online:
|
||||
ca-online-app-secret: adf718ebc1fb4bb7b158de9117d1313a
|
||||
ca-online-api-url: http://testmicrosrv.scca.com.cn:9527
|
||||
|
||||
ca-online-v2:
|
||||
ca-online-app-id: SCCA1951838127584579586
|
||||
ca-online-app-secret: a26bf6c8b78b4099939ab09accbc9a80
|
||||
ca-online-api-url: http://testmicrosrv.scca.com.cn:9527
|
||||
|
||||
# [腾讯im]
|
||||
#im:
|
||||
# im-app-id: 1400798221
|
||||
|
||||
@ -46,6 +46,11 @@ ca-online:
|
||||
ca-online-app-secret:
|
||||
ca-online-api-url:
|
||||
|
||||
ca-online-v2:
|
||||
ca-online-app-id:
|
||||
ca-online-app-secret:
|
||||
ca-online-api-url:
|
||||
|
||||
# [腾讯im]
|
||||
im:
|
||||
im-app-id:
|
||||
|
||||
7
config/caOnlineV2.go
Normal file
7
config/caOnlineV2.go
Normal file
@ -0,0 +1,7 @@
|
||||
package config
|
||||
|
||||
type CaOnlineV2 struct {
|
||||
CaOnlineAppID string `mapstructure:"ca-online-app-id" json:"ca-online-app-id" yaml:"ca-online-app-id"`
|
||||
CaOnlineAppSecret string `mapstructure:"ca-online-app-secret" json:"ca-online-app-secret" yaml:"ca-online-app-secret"`
|
||||
CaOnlineAPIURL string `mapstructure:"ca-online-api-url" json:"ca-online-api-url" yaml:"ca-online-api-url"`
|
||||
}
|
||||
@ -12,6 +12,7 @@ type Config struct {
|
||||
Oss Oss `mapstructure:"oss" json:"oss" yaml:"oss"`
|
||||
Snowflake int64 `mapstructure:"snowflake" json:"snowflake" yaml:"snowflake"`
|
||||
CaOnline CaOnline `mapstructure:"ca-online" json:"ca-online" yaml:"ca-online"`
|
||||
CaOnlineV2 CaOnlineV2 `mapstructure:"ca-online-v2" json:"ca-online-v2" yaml:"ca-online-v2"`
|
||||
Im Im `mapstructure:"im" json:"im" yaml:"im"`
|
||||
Dysms Dysms `mapstructure:"dysms" json:"dysms" yaml:"dysms"`
|
||||
Wechat Wechat `mapstructure:"wechat" json:"wechat" yaml:"wechat"`
|
||||
|
||||
57
extend/ca_v2/auth.go
Normal file
57
extend/ca_v2/auth.go
Normal file
@ -0,0 +1,57 @@
|
||||
package cav2
|
||||
|
||||
import "net/url"
|
||||
|
||||
func (c *Client) CheckCIdAndName(request CheckCIdAndNameRequest) error {
|
||||
form := url.Values{}
|
||||
setValue(form, "requestId", request.RequestID)
|
||||
setValue(form, "cName", request.CName)
|
||||
setValue(form, "cId", request.CID)
|
||||
return c.postForm("/scca-ocr-server/authapi/checkCIdAndName", form, nil)
|
||||
}
|
||||
|
||||
func (c *Client) FetchH5FaceURL(request FetchH5FaceURLRequest) (string, error) {
|
||||
form := url.Values{}
|
||||
setValue(form, "cName", request.CName)
|
||||
setValue(form, "cId", request.CID)
|
||||
setValue(form, "orderNo", request.OrderNo)
|
||||
setValue(form, "redirectUrl", request.RedirectURL)
|
||||
return c.postFormForString("/scca-ocr-server/authapi/fetchH5FaceUrl", form)
|
||||
}
|
||||
|
||||
func (c *Client) FetchH5FaceResult(request FetchH5FaceResultRequest) (*FetchH5FaceResultResponse, error) {
|
||||
form := url.Values{}
|
||||
setValue(form, "orderNo", request.OrderNo)
|
||||
|
||||
response := &FetchH5FaceResultResponse{}
|
||||
if err := c.postForm("/scca-ocr-server/authapi/fetchH5FaceResult", form, response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (c *Client) CheckEnterprise(request CheckEnterpriseRequest) error {
|
||||
form := url.Values{}
|
||||
setValue(form, "cName", request.CName)
|
||||
setValue(form, "cId", request.CID)
|
||||
setValue(form, "registrationNumber", request.RegistrationNumber)
|
||||
setValue(form, "enterpriseName", request.EnterpriseName)
|
||||
return c.postForm("/scca-ocr-server/authapi/checkEnterprise", form, nil)
|
||||
}
|
||||
|
||||
func (c *Client) CheckEnterprise3(request CheckEnterprise3Request) error {
|
||||
form := url.Values{}
|
||||
setValue(form, "cName", request.CName)
|
||||
setValue(form, "registrationNumber", request.RegistrationNumber)
|
||||
setValue(form, "enterpriseName", request.EnterpriseName)
|
||||
setValue(form, "requestId", request.RequestID)
|
||||
return c.postForm("/scca-ocr-server/authapi/checkDeptAndIndividualInfo3", form, nil)
|
||||
}
|
||||
|
||||
func (c *Client) PhoneVerification(request PhoneVerificationRequest) error {
|
||||
form := url.Values{}
|
||||
setValue(form, "realName", request.RealName)
|
||||
setValue(form, "idCard", request.IDCard)
|
||||
setValue(form, "phoneNumber", request.PhoneNumber)
|
||||
return c.postForm("/scca-ocr-server/authapi/phoneVerification", form, nil)
|
||||
}
|
||||
67
extend/ca_v2/certificate.go
Normal file
67
extend/ca_v2/certificate.go
Normal file
@ -0,0 +1,67 @@
|
||||
package cav2
|
||||
|
||||
import "net/url"
|
||||
|
||||
func (c *Client) GetServiceURL(request GetServiceURLRequest) (*GetServiceURLResponse, error) {
|
||||
form := url.Values{}
|
||||
setValue(form, "certType", request.CertType)
|
||||
setValue(form, "type", request.Type)
|
||||
setValue(form, "entityId", request.EntityID)
|
||||
setValue(form, "userName", request.UserName)
|
||||
setValue(form, "userIdCard", request.UserIDCard)
|
||||
setValue(form, "userPhone", request.UserPhone)
|
||||
setValue(form, "email", request.Email)
|
||||
setValue(form, "enterpriseName", request.EnterpriseName)
|
||||
setValue(form, "socialCreditCode", request.SocialCreditCode)
|
||||
setValue(form, "deptPerson", request.DeptPerson)
|
||||
setValue(form, "deptPersonCode", request.DeptPersonCode)
|
||||
setValue(form, "deptPersonPhone", request.DeptPersonPhone)
|
||||
setValue(form, "agentName", request.AgentName)
|
||||
setValue(form, "agentIdCard", request.AgentIDCard)
|
||||
setValue(form, "authMethod", request.AuthMethod)
|
||||
|
||||
response := &GetServiceURLResponse{}
|
||||
if err := c.postForm("/open/api/data/getServiceUrl", form, response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (c *Client) CertSign(request CertSignRequest) (*CertSignResponse, error) {
|
||||
form := url.Values{}
|
||||
setValue(form, "entityId", request.EntityID)
|
||||
setValue(form, "requestId", request.RequestID)
|
||||
setValue(form, "businessType", request.BusinessType)
|
||||
setValue(form, "toSign", request.ToSign)
|
||||
setValue(form, "notifyUrl", request.NotifyURL)
|
||||
setValue(form, "jumpUrl", request.JumpURL)
|
||||
setValue(form, "extParam", request.ExtParam)
|
||||
|
||||
response := &CertSignResponse{}
|
||||
if err := c.postForm("/open/api/data/certSign", form, response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (c *Client) GetCertSignResult(request GetCertSignResultRequest) (*GetCertSignResultResponse, error) {
|
||||
form := url.Values{}
|
||||
setValue(form, "requestId", request.RequestID)
|
||||
|
||||
response := &GetCertSignResultResponse{}
|
||||
if err := c.postForm("/open/api/data/getCertSignResult", form, response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (c *Client) PasswordLessSignInfo(request PasswordLessSignInfoRequest) (*PasswordLessSignInfoResponse, error) {
|
||||
form := url.Values{}
|
||||
setValue(form, "entityId", request.EntityID)
|
||||
|
||||
response := &PasswordLessSignInfoResponse{}
|
||||
if err := c.postForm("/open/api/data/passwordLessSignInfo", form, response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
174
extend/ca_v2/client.go
Normal file
174
extend/ca_v2/client.go
Normal file
@ -0,0 +1,174 @@
|
||||
package cav2
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hospital-admin-api/config"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
BaseURL string
|
||||
AppID string
|
||||
AppSecret string
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
func NewClient(baseURL, appID, appSecret string) *Client {
|
||||
return &Client{
|
||||
BaseURL: strings.TrimRight(baseURL, "/"),
|
||||
AppID: appID,
|
||||
AppSecret: appSecret,
|
||||
HTTPClient: &http.Client{
|
||||
Timeout: 15 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func NewClientFromConfig() *Client {
|
||||
baseURL := config.C.CaOnlineV2.CaOnlineAPIURL
|
||||
appID := config.C.CaOnlineV2.CaOnlineAppID
|
||||
appSecret := config.C.CaOnlineV2.CaOnlineAppSecret
|
||||
|
||||
if baseURL == "" {
|
||||
baseURL = config.C.CaOnline.CaOnlineApiUrl
|
||||
}
|
||||
if appID == "" {
|
||||
appID = config.C.CaOnline.CaOnlineAppId
|
||||
}
|
||||
if appSecret == "" {
|
||||
appSecret = config.C.CaOnline.CaOnlineAppSecret
|
||||
}
|
||||
|
||||
return NewClient(
|
||||
baseURL,
|
||||
appID,
|
||||
appSecret,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Client) postForm(path string, form url.Values, out interface{}) error {
|
||||
if c == nil {
|
||||
return errors.New("ca v2 client is nil")
|
||||
}
|
||||
if c.BaseURL == "" || c.AppID == "" || c.AppSecret == "" {
|
||||
return errors.New("ca v2 client config is incomplete")
|
||||
}
|
||||
if c.HTTPClient == nil {
|
||||
c.HTTPClient = &http.Client{Timeout: 15 * time.Second}
|
||||
}
|
||||
|
||||
payload := strings.NewReader(form.Encode())
|
||||
req, err := http.NewRequest(http.MethodPost, c.BaseURL+path, payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.Header.Set("app_id", c.AppID)
|
||||
req.Header.Set("signature", c.signature(form))
|
||||
|
||||
resp, err := c.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("ca v2 request failed: http %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var envelope responseEnvelope
|
||||
if err := json.Unmarshal(respBody, &envelope); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if envelope.ResultCode != 0 {
|
||||
if envelope.ResultMsg != "" {
|
||||
return errors.New(envelope.ResultMsg)
|
||||
}
|
||||
return fmt.Errorf("ca v2 request failed with code %d", envelope.ResultCode)
|
||||
}
|
||||
|
||||
if out == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
body := strings.TrimSpace(string(envelope.Body))
|
||||
if body == "" || body == "null" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return json.Unmarshal(envelope.Body, out)
|
||||
}
|
||||
|
||||
func (c *Client) postFormForString(path string, form url.Values) (string, error) {
|
||||
var raw json.RawMessage
|
||||
if err := c.postForm(path, form, &raw); err != nil {
|
||||
return "", err
|
||||
}
|
||||
body := strings.TrimSpace(string(raw))
|
||||
if body == "" || body == "null" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
var text string
|
||||
if err := json.Unmarshal(raw, &text); err == nil {
|
||||
return text, nil
|
||||
}
|
||||
|
||||
var data map[string]string
|
||||
if err := json.Unmarshal(raw, &data); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, key := range []string{"url", "authUrl", "faceUrl", "h5Url"} {
|
||||
if value := strings.TrimSpace(data[key]); value != "" {
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", errors.New("ca v2 string body not found")
|
||||
}
|
||||
|
||||
func (c *Client) signature(form url.Values) string {
|
||||
keys := make([]string, 0, len(form))
|
||||
for key := range form {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
values := make([]string, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
for _, value := range form[key] {
|
||||
values = append(values, value)
|
||||
}
|
||||
}
|
||||
|
||||
h := hmac.New(sha1.New, []byte(c.AppSecret))
|
||||
h.Write([]byte(strings.Join(values, "&")))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func setValue(form url.Values, key, value string) {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
return
|
||||
}
|
||||
form.Set(key, value)
|
||||
}
|
||||
116
extend/ca_v2/client_test.go
Normal file
116
extend/ca_v2/client_test.go
Normal file
@ -0,0 +1,116 @@
|
||||
package cav2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetServiceURL(t *testing.T) {
|
||||
var gotForm url.Values
|
||||
signClient := NewClient("http://unused", "test-app", "test-secret")
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/open/api/data/getServiceUrl" {
|
||||
t.Fatalf("unexpected path: %s", r.URL.Path)
|
||||
}
|
||||
if r.Method != http.MethodPost {
|
||||
t.Fatalf("unexpected method: %s", r.Method)
|
||||
}
|
||||
if err := r.ParseForm(); err != nil {
|
||||
t.Fatalf("parse form: %v", err)
|
||||
}
|
||||
gotForm = r.PostForm
|
||||
|
||||
expectedSignature := signClient.signature(r.PostForm)
|
||||
if r.Header.Get("app_id") != "test-app" {
|
||||
t.Fatalf("unexpected app_id: %s", r.Header.Get("app_id"))
|
||||
}
|
||||
if r.Header.Get("signature") != expectedSignature {
|
||||
t.Fatalf("unexpected signature: %s", r.Header.Get("signature"))
|
||||
}
|
||||
|
||||
_ = json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"result_code": 0,
|
||||
"result_msg": "",
|
||||
"success": true,
|
||||
"body": map[string]string{
|
||||
"webToken": "token-1",
|
||||
"url": "https://example.test/h5",
|
||||
"expireTime": "2026-05-01 10:00:00",
|
||||
},
|
||||
})
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient(server.URL, "test-app", "test-secret")
|
||||
response, err := client.GetServiceURL(GetServiceURLRequest{
|
||||
CertType: "Personal",
|
||||
Type: "CERT_APPLY",
|
||||
EntityID: "user-1",
|
||||
UserName: "Alice",
|
||||
UserIDCard: "123456",
|
||||
UserPhone: "13800000000",
|
||||
AuthMethod: "face",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("GetServiceURL error: %v", err)
|
||||
}
|
||||
|
||||
if gotForm.Get("entityId") != "user-1" {
|
||||
t.Fatalf("unexpected entityId: %s", gotForm.Get("entityId"))
|
||||
}
|
||||
if response.WebToken != "token-1" || response.URL != "https://example.test/h5" {
|
||||
t.Fatalf("unexpected response: %+v", response)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFetchH5FaceURLWithStringBody(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_ = json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"result_code": 0,
|
||||
"result_msg": "",
|
||||
"success": true,
|
||||
"body": "https://example.test/face-h5",
|
||||
})
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient(server.URL, "test-app", "test-secret")
|
||||
link, err := client.FetchH5FaceURL(FetchH5FaceURLRequest{
|
||||
CName: "Alice",
|
||||
CID: "123456",
|
||||
OrderNo: "order-1",
|
||||
RedirectURL: "https://callback.test/result",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("FetchH5FaceURL error: %v", err)
|
||||
}
|
||||
if link != "https://example.test/face-h5" {
|
||||
t.Fatalf("unexpected link: %s", link)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckEnterpriseSuccessWithoutBody(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_ = json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"result_code": 0,
|
||||
"result_msg": "",
|
||||
"success": true,
|
||||
"body": nil,
|
||||
})
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient(server.URL, "test-app", "test-secret")
|
||||
err := client.CheckEnterprise(CheckEnterpriseRequest{
|
||||
CName: "法人A",
|
||||
CID: "510000000000000000",
|
||||
RegistrationNumber: "91510000TEST",
|
||||
EnterpriseName: "测试企业",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CheckEnterprise error: %v", err)
|
||||
}
|
||||
}
|
||||
129
extend/ca_v2/types.go
Normal file
129
extend/ca_v2/types.go
Normal file
@ -0,0 +1,129 @@
|
||||
package cav2
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type responseEnvelope struct {
|
||||
ResultCode int `json:"result_code"`
|
||||
ResultMsg string `json:"result_msg"`
|
||||
Success bool `json:"success"`
|
||||
Body json.RawMessage `json:"body"`
|
||||
}
|
||||
|
||||
type GetServiceURLRequest struct {
|
||||
CertType string `json:"certType"`
|
||||
Type string `json:"type"`
|
||||
EntityID string `json:"entityId"`
|
||||
UserName string `json:"userName,omitempty"`
|
||||
UserIDCard string `json:"userIdCard,omitempty"`
|
||||
UserPhone string `json:"userPhone,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
EnterpriseName string `json:"enterpriseName,omitempty"`
|
||||
SocialCreditCode string `json:"socialCreditCode,omitempty"`
|
||||
DeptPerson string `json:"deptPerson,omitempty"`
|
||||
DeptPersonCode string `json:"deptPersonCode,omitempty"`
|
||||
DeptPersonPhone string `json:"deptPersonPhone,omitempty"`
|
||||
AgentName string `json:"agentName,omitempty"`
|
||||
AgentIDCard string `json:"agentIdCard,omitempty"`
|
||||
AuthMethod string `json:"authMethod,omitempty"`
|
||||
}
|
||||
|
||||
type GetServiceURLResponse struct {
|
||||
WebToken string `json:"webToken"`
|
||||
URL string `json:"url"`
|
||||
ExpireTime string `json:"expireTime"`
|
||||
}
|
||||
|
||||
type CertSignRequest struct {
|
||||
EntityID string `json:"entityId"`
|
||||
RequestID string `json:"requestId"`
|
||||
BusinessType string `json:"businessType"`
|
||||
ToSign string `json:"toSign"`
|
||||
NotifyURL string `json:"notifyUrl,omitempty"`
|
||||
JumpURL string `json:"jumpUrl,omitempty"`
|
||||
ExtParam string `json:"extParam,omitempty"`
|
||||
}
|
||||
|
||||
type CertSignResponse struct {
|
||||
RequestID string `json:"requestId"`
|
||||
BusinessType string `json:"businessType"`
|
||||
SignType string `json:"signType"`
|
||||
SignP7 string `json:"signP7"`
|
||||
ToSignEncoding string `json:"toSignEncoding"`
|
||||
SignURL string `json:"signUrl"`
|
||||
}
|
||||
|
||||
type GetCertSignResultRequest struct {
|
||||
RequestID string `json:"requestId"`
|
||||
}
|
||||
|
||||
type GetCertSignResultResponse struct {
|
||||
RequestID string `json:"requestId"`
|
||||
BusinessType string `json:"businessType"`
|
||||
SignType string `json:"signType"`
|
||||
SignP7 string `json:"signP7"`
|
||||
ToSignEncoding string `json:"toSignEncoding"`
|
||||
}
|
||||
|
||||
type PasswordLessSignInfoRequest struct {
|
||||
EntityID string `json:"entityId"`
|
||||
}
|
||||
|
||||
type PasswordLessSignInfoResponse struct {
|
||||
CertDN string `json:"certDn"`
|
||||
NotAfter string `json:"notafter"`
|
||||
CertSN string `json:"certSn"`
|
||||
EnableTime string `json:"enableTime"`
|
||||
DisableTime string `json:"disableTime"`
|
||||
DataStatus int `json:"dataStatus"`
|
||||
}
|
||||
|
||||
type CheckCIdAndNameRequest struct {
|
||||
RequestID string `json:"requestId"`
|
||||
CName string `json:"cName"`
|
||||
CID string `json:"cId"`
|
||||
}
|
||||
|
||||
type FetchH5FaceURLRequest struct {
|
||||
CName string `json:"cName"`
|
||||
CID string `json:"cId"`
|
||||
OrderNo string `json:"orderNo"`
|
||||
RedirectURL string `json:"redirectUrl"`
|
||||
}
|
||||
|
||||
type FetchH5FaceResultRequest struct {
|
||||
OrderNo string `json:"orderNo"`
|
||||
}
|
||||
|
||||
type FetchH5FaceResultResponse struct {
|
||||
Code string `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
LiveRate string `json:"liveRate"`
|
||||
Similarity string `json:"similarity"`
|
||||
OccurredTime string `json:"occurredTime"`
|
||||
OrderNo string `json:"orderNo"`
|
||||
IDNo string `json:"idNo"`
|
||||
Name string `json:"name"`
|
||||
IDType string `json:"idType"`
|
||||
Photo string `json:"photo"`
|
||||
Video string `json:"video"`
|
||||
}
|
||||
|
||||
type CheckEnterpriseRequest struct {
|
||||
CName string `json:"cName"`
|
||||
CID string `json:"cId"`
|
||||
RegistrationNumber string `json:"registrationNumber"`
|
||||
EnterpriseName string `json:"enterpriseName"`
|
||||
}
|
||||
|
||||
type CheckEnterprise3Request struct {
|
||||
CName string `json:"cName"`
|
||||
RegistrationNumber string `json:"registrationNumber"`
|
||||
EnterpriseName string `json:"enterpriseName"`
|
||||
RequestID string `json:"requestId,omitempty"`
|
||||
}
|
||||
|
||||
type PhoneVerificationRequest struct {
|
||||
RealName string `json:"realName"`
|
||||
IDCard string `json:"idCard"`
|
||||
PhoneNumber string `json:"phoneNumber"`
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user