Add service_kyc
All checks were successful
Client CMS Check Build (NixCN CMS) TeamCity build finished
Backend Check Build (NixCN CMS) TeamCity build finished

Signed-off-by: Asai Neko <sugar@sne.moe>
This commit is contained in:
2026-02-01 13:15:17 +08:00
parent a2eb882398
commit 0ac96ab3e6
23 changed files with 1831 additions and 89 deletions

View File

@@ -2,10 +2,20 @@ package kyc
import (
"nixcn-cms/middleware"
"nixcn-cms/service/service_kyc"
"github.com/gin-gonic/gin"
)
func ApiHandler(r *gin.RouterGroup) {
r.Use(middleware.ApiVersionCheck(), middleware.JWTAuth(), middleware.Permission(10))
type KycHandler struct {
svc service_kyc.KycService
}
func ApiHandler(r *gin.RouterGroup) {
kycSvc := service_kyc.NewKycService()
kycHandler := &KycHandler{kycSvc}
r.Use(middleware.ApiVersionCheck(), middleware.JWTAuth(), middleware.Permission(10))
r.POST("/kyc/session", kycHandler.Session)
r.POST("/kyc/query", kycHandler.Query)
}

66
api/kyc/query.go Normal file
View File

@@ -0,0 +1,66 @@
package kyc
import (
"nixcn-cms/internal/exception"
"nixcn-cms/service/service_kyc"
"nixcn-cms/utils"
"github.com/gin-gonic/gin"
)
// @Summary Query KYC Status
// @Description Checks the current state of a KYC session and updates local database if approved.
// @Tags KYC
// @Accept json
// @Produce json
// @Param payload body service_kyc.KycQueryData true "KYC query data (KycId)"
// @Success 200 {object} utils.RespStatus{data=service_kyc.KycQueryResponse} "Query processed (success/pending/failed)"
// @Failure 400 {object} utils.RespStatus{data=nil} "Invalid UUID or input"
// @Failure 403 {object} utils.RespStatus{data=nil} "Unauthorized"
// @Failure 500 {object} utils.RespStatus{data=nil} "Internal Server Error"
// @Security ApiKeyAuth
// @Router /kyc/query [post]
func (self *KycHandler) Query(c *gin.Context) {
_, ok := c.Get("user_id")
if !ok {
errorCode := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceQuery).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorMissingUserId).
Throw(c).
String()
utils.HttpResponse(c, 403, errorCode)
return
}
var queryData service_kyc.KycQueryData
if err := c.ShouldBindJSON(&queryData); err != nil {
errorCode := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceQuery).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
SetError(err).
Throw(c).
String()
utils.HttpResponse(c, 400, errorCode)
return
}
queryPayload := &service_kyc.KycQueryPayload{
Context: c,
Data: &queryData,
}
result := self.svc.QueryKyc(queryPayload)
if result.Common.Exception.Original != exception.CommonSuccess {
utils.HttpResponse(c, result.Common.HttpCode, result.Common.Exception.String())
return
}
utils.HttpResponse(c, result.Common.HttpCode, result.Common.Exception.String(), result.Data)
}

68
api/kyc/session.go Normal file
View File

@@ -0,0 +1,68 @@
package kyc
import (
"nixcn-cms/internal/exception"
"nixcn-cms/service/service_kyc"
"nixcn-cms/utils"
"github.com/gin-gonic/gin"
)
// @Summary Create KYC Session
// @Description Initializes a KYC process (CNRid or Passport) and returns the status or redirect URI.
// @Tags KYC
// @Accept json
// @Produce json
// @Param payload body service_kyc.KycSessionData true "KYC session data (Type and Base64 Identity)"
// @Success 200 {object} utils.RespStatus{data=service_kyc.KycSessionResponse} "Session created successfully"
// @Failure 400 {object} utils.RespStatus{data=nil} "Invalid input or decode failed"
// @Failure 403 {object} utils.RespStatus{data=nil} "Missing User ID"
// @Failure 500 {object} utils.RespStatus{data=nil} "Internal Server Error / KYC Service Error"
// @Security ApiKeyAuth
// @Router /kyc/session [post]
func (self *KycHandler) Session(c *gin.Context) {
userIdFromHeaderOrig, ok := c.Get("user_id")
if !ok {
errorCode := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceSession).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorMissingUserId).
Throw(c).
String()
utils.HttpResponse(c, 403, errorCode)
return
}
var sessionData service_kyc.KycSessionData
if err := c.ShouldBindJSON(&sessionData); err != nil {
errorCode := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceSession).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
SetError(err).
Throw(c).
String()
utils.HttpResponse(c, 400, errorCode)
return
}
sessionData.UserId = userIdFromHeaderOrig.(string)
kycPayload := &service_kyc.KycSessionPayload{
Context: c,
Data: &sessionData,
}
result := self.svc.SessionKyc(kycPayload)
if result.Common.Exception.Original != exception.CommonSuccess {
utils.HttpResponse(c, result.Common.HttpCode, result.Common.Exception.String())
return
}
utils.HttpResponse(c, result.Common.HttpCode, result.Common.Exception.String(), result.Data)
}

View File

@@ -10,3 +10,5 @@ common:
missing_user_id: "00007"
user_not_found: "00008"
user_not_public: "00009"
base64_decode_failed: "00010"
json_decode_failed: "00011"

View File

@@ -21,5 +21,9 @@ endpoint:
list: "03"
full: "04"
create: "05"
kyc:
service:
session: "01"
query: "02"
middleware:
service: "01"

View File

@@ -2,3 +2,4 @@ service:
auth: "001"
user: "002"
event: "003"
kyc: "004"

View File

@@ -36,3 +36,8 @@ event:
database_failed: "00001"
join:
event_invalid: "00001"
kyc:
session:
failed: "00001"
query:
failed: "00001"

View File

@@ -19,9 +19,9 @@ type Attendance struct {
AttendanceId uuid.UUID `json:"attendance_id" gorm:"type:uuid;uniqueIndex;not null"`
EventId uuid.UUID `json:"event_id" gorm:"type:uuid;uniqueIndex:unique_event_user;not null"`
UserId uuid.UUID `json:"user_id" gorm:"type:uuid;uniqueIndex:unique_event_user;not null"`
KycId uuid.UUID `json:"kyc_id" gorm:"type:uuid;uniqueIndex:unique_event_user;not null"`
Role string `json:"role" gorm:"type:varchar(255);not null"`
State string `json:"state" gorm:"type:varchar(255);not null"` // suspended | out_of_limit | success
KycInfo string `json:"kyc_info" gorm:"type:text"`
CheckinAt time.Time `json:"checkin_at"`
}
@@ -43,6 +43,11 @@ func (self *Attendance) SetUserId(s uuid.UUID) *Attendance {
return self
}
func (self *Attendance) SetKycId(s uuid.UUID) *Attendance {
self.KycId = s
return self
}
func (self *Attendance) SetRole(s string) *Attendance {
self.Role = s
return self
@@ -53,11 +58,6 @@ func (self *Attendance) SetState(s string) *Attendance {
return self
}
func (self *Attendance) SetKycInfo(s string) *Attendance {
self.KycInfo = s
return self
}
func (self *Attendance) GetAttendance(ctx context.Context, userId, eventId uuid.UUID) (*Attendance, error) {
var checkin Attendance
@@ -112,6 +112,23 @@ func (self *Attendance) GetEventsByUserID(ctx context.Context, userID uuid.UUID)
return &result, err
}
func (self *Attendance) GetAttendanceByEventIdAndUserId(ctx context.Context, eventId, userId uuid.UUID) (*Attendance, error) {
var attendance Attendance
err := Database.WithContext(ctx).
Where("event_id = ? AND user_id = ?", eventId, userId).
First(&attendance).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &attendance, nil
}
func (self *Attendance) Create(ctx context.Context) error {
self.UUID = uuid.New()
self.AttendanceId = uuid.New()

92
data/kyc.go Normal file
View File

@@ -0,0 +1,92 @@
package data
import (
"context"
"github.com/google/uuid"
"gorm.io/gorm"
)
type Kyc struct {
Id uint `json:"id" gorm:"primarykey;autoincrement"`
UUID uuid.UUID `json:"uuid" gorm:"type:uuid;uniqueindex;not null"`
UserId uuid.UUID `json:"user_id" gorm:"type:uuid;not null"` // 已移除 uniqueindex
KycId uuid.UUID `json:"kyc_id" gorm:"type:uuid;uniqueindex;not null"`
Type string `json:"type" gorm:"type:varchar(255);not null"`
KycInfo string `json:"kyc_info" gorm:"type:text"` // aes256(base64)
}
func (self *Kyc) SetUserId(id uuid.UUID) *Kyc {
self.UserId = id
return self
}
func (self *Kyc) SetType(t string) *Kyc {
self.Type = t
return self
}
func (self *Kyc) SetKycInfo(info string) *Kyc {
self.KycInfo = info
return self
}
func (self *Kyc) Create(ctx context.Context) (uuid.UUID, error) {
self.UUID = uuid.New()
self.KycId = uuid.New()
err := Database.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
return tx.Create(self).Error
})
if err != nil {
return uuid.Nil, err
}
return self.KycId, nil
}
func (self *Kyc) GetByKycId(ctx context.Context, kycId *uuid.UUID) (*Kyc, error) {
var kyc Kyc
err := Database.WithContext(ctx).
Where("kyc_id = ?", kycId).
First(&kyc).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &kyc, nil
}
func (self *Kyc) ListByUserId(ctx context.Context, userId *uuid.UUID) ([]Kyc, error) {
var list []Kyc
err := Database.WithContext(ctx).
Where("user_id = ?", userId).
Find(&list).Error
return list, err
}
func (self *Kyc) UpdateByKycID(ctx context.Context, kycId *uuid.UUID, updates map[string]any) error {
return Database.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
return tx.Model(&Kyc{}).
Where("kyc_id = ?", kycId).
Updates(updates).Error
})
}
func (self *Kyc) DeleteByKycID(ctx context.Context, kycId *uuid.UUID) error {
return Database.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
return tx.Where("kyc_id = ?", kycId).
Delete(&Kyc{}).Error
})
}
func (self *Kyc) DeleteAllByUserId(ctx context.Context, userId *uuid.UUID) error {
return Database.WithContext(ctx).
Where("user_id = ?", userId).
Delete(&Kyc{}).Error
}

View File

@@ -19,6 +19,7 @@ type User struct {
Bio string `json:"bio" gorm:"type:text"`
PermissionLevel uint `json:"permission_level" gorm:"default:10;not null"`
AllowPublic bool `json:"allow_public" gorm:"default:false;not null"`
KycInfo string `json:"kyc_info" gorm:"type:text"`
}
type UserIndexDoc struct {

View File

@@ -1144,6 +1144,216 @@ const docTemplate = `{
}
}
},
"/kyc/query": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Checks the current state of a KYC session and updates local database if approved.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"KYC"
],
"summary": "Query KYC Status",
"parameters": [
{
"description": "KYC query data (KycId)",
"name": "payload",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/service_kyc.KycQueryData"
}
}
],
"responses": {
"200": {
"description": "Query processed (success/pending/failed)",
"schema": {
"allOf": [
{
"$ref": "#/definitions/utils.RespStatus"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/service_kyc.KycQueryResponse"
}
}
}
]
}
},
"400": {
"description": "Invalid UUID or input",
"schema": {
"allOf": [
{
"$ref": "#/definitions/utils.RespStatus"
},
{
"type": "object",
"properties": {
"data": {
"type": "object"
}
}
}
]
}
},
"403": {
"description": "Unauthorized",
"schema": {
"allOf": [
{
"$ref": "#/definitions/utils.RespStatus"
},
{
"type": "object",
"properties": {
"data": {
"type": "object"
}
}
}
]
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"allOf": [
{
"$ref": "#/definitions/utils.RespStatus"
},
{
"type": "object",
"properties": {
"data": {
"type": "object"
}
}
}
]
}
}
}
}
},
"/kyc/session": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Initializes a KYC process (CNRid or Passport) and returns the status or redirect URI.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"KYC"
],
"summary": "Create KYC Session",
"parameters": [
{
"description": "KYC session data (Type and Base64 Identity)",
"name": "payload",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/service_kyc.KycSessionData"
}
}
],
"responses": {
"200": {
"description": "Session created successfully",
"schema": {
"allOf": [
{
"$ref": "#/definitions/utils.RespStatus"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/service_kyc.KycSessionResponse"
}
}
}
]
}
},
"400": {
"description": "Invalid input or decode failed",
"schema": {
"allOf": [
{
"$ref": "#/definitions/utils.RespStatus"
},
{
"type": "object",
"properties": {
"data": {
"type": "object"
}
}
}
]
}
},
"403": {
"description": "Missing User ID",
"schema": {
"allOf": [
{
"$ref": "#/definitions/utils.RespStatus"
},
{
"type": "object",
"properties": {
"data": {
"type": "object"
}
}
}
]
}
},
"500": {
"description": "Internal Server Error / KYC Service Error",
"schema": {
"allOf": [
{
"$ref": "#/definitions/utils.RespStatus"
},
{
"type": "object",
"properties": {
"data": {
"type": "object"
}
}
}
]
}
}
}
}
},
"/user/info": {
"get": {
"security": [
@@ -1739,6 +1949,54 @@ const docTemplate = `{
"properties": {
"event_id": {
"type": "string"
},
"kyc_id": {
"type": "string"
}
}
},
"service_kyc.KycQueryData": {
"type": "object",
"properties": {
"kyc_id": {
"type": "string"
}
}
},
"service_kyc.KycQueryResponse": {
"type": "object",
"properties": {
"status": {
"description": "success | pending | failed",
"type": "string"
}
}
},
"service_kyc.KycSessionData": {
"type": "object",
"properties": {
"identity": {
"description": "base64 json",
"type": "string"
},
"type": {
"description": "cnrid | passport",
"type": "string"
}
}
},
"service_kyc.KycSessionResponse": {
"type": "object",
"properties": {
"kyc_id": {
"type": "string"
},
"redirect_uri": {
"type": "string"
},
"status": {
"description": "success | processing",
"type": "string"
}
}
},

View File

@@ -1142,6 +1142,216 @@
}
}
},
"/kyc/query": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Checks the current state of a KYC session and updates local database if approved.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"KYC"
],
"summary": "Query KYC Status",
"parameters": [
{
"description": "KYC query data (KycId)",
"name": "payload",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/service_kyc.KycQueryData"
}
}
],
"responses": {
"200": {
"description": "Query processed (success/pending/failed)",
"schema": {
"allOf": [
{
"$ref": "#/definitions/utils.RespStatus"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/service_kyc.KycQueryResponse"
}
}
}
]
}
},
"400": {
"description": "Invalid UUID or input",
"schema": {
"allOf": [
{
"$ref": "#/definitions/utils.RespStatus"
},
{
"type": "object",
"properties": {
"data": {
"type": "object"
}
}
}
]
}
},
"403": {
"description": "Unauthorized",
"schema": {
"allOf": [
{
"$ref": "#/definitions/utils.RespStatus"
},
{
"type": "object",
"properties": {
"data": {
"type": "object"
}
}
}
]
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"allOf": [
{
"$ref": "#/definitions/utils.RespStatus"
},
{
"type": "object",
"properties": {
"data": {
"type": "object"
}
}
}
]
}
}
}
}
},
"/kyc/session": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Initializes a KYC process (CNRid or Passport) and returns the status or redirect URI.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"KYC"
],
"summary": "Create KYC Session",
"parameters": [
{
"description": "KYC session data (Type and Base64 Identity)",
"name": "payload",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/service_kyc.KycSessionData"
}
}
],
"responses": {
"200": {
"description": "Session created successfully",
"schema": {
"allOf": [
{
"$ref": "#/definitions/utils.RespStatus"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/service_kyc.KycSessionResponse"
}
}
}
]
}
},
"400": {
"description": "Invalid input or decode failed",
"schema": {
"allOf": [
{
"$ref": "#/definitions/utils.RespStatus"
},
{
"type": "object",
"properties": {
"data": {
"type": "object"
}
}
}
]
}
},
"403": {
"description": "Missing User ID",
"schema": {
"allOf": [
{
"$ref": "#/definitions/utils.RespStatus"
},
{
"type": "object",
"properties": {
"data": {
"type": "object"
}
}
}
]
}
},
"500": {
"description": "Internal Server Error / KYC Service Error",
"schema": {
"allOf": [
{
"$ref": "#/definitions/utils.RespStatus"
},
{
"type": "object",
"properties": {
"data": {
"type": "object"
}
}
}
]
}
}
}
}
},
"/user/info": {
"get": {
"security": [
@@ -1737,6 +1947,54 @@
"properties": {
"event_id": {
"type": "string"
},
"kyc_id": {
"type": "string"
}
}
},
"service_kyc.KycQueryData": {
"type": "object",
"properties": {
"kyc_id": {
"type": "string"
}
}
},
"service_kyc.KycQueryResponse": {
"type": "object",
"properties": {
"status": {
"description": "success | pending | failed",
"type": "string"
}
}
},
"service_kyc.KycSessionData": {
"type": "object",
"properties": {
"identity": {
"description": "base64 json",
"type": "string"
},
"type": {
"description": "cnrid | passport",
"type": "string"
}
}
},
"service_kyc.KycSessionResponse": {
"type": "object",
"properties": {
"kyc_id": {
"type": "string"
},
"redirect_uri": {
"type": "string"
},
"status": {
"description": "success | processing",
"type": "string"
}
}
},

View File

@@ -104,6 +104,38 @@ definitions:
properties:
event_id:
type: string
kyc_id:
type: string
type: object
service_kyc.KycQueryData:
properties:
kyc_id:
type: string
type: object
service_kyc.KycQueryResponse:
properties:
status:
description: success | pending | failed
type: string
type: object
service_kyc.KycSessionData:
properties:
identity:
description: base64 json
type: string
type:
description: cnrid | passport
type: string
type: object
service_kyc.KycSessionResponse:
properties:
kyc_id:
type: string
redirect_uri:
type: string
status:
description: success | processing
type: string
type: object
service_user.UserInfoData:
properties:
@@ -769,6 +801,120 @@ paths:
summary: List Events
tags:
- Event
/kyc/query:
post:
consumes:
- application/json
description: Checks the current state of a KYC session and updates local database
if approved.
parameters:
- description: KYC query data (KycId)
in: body
name: payload
required: true
schema:
$ref: '#/definitions/service_kyc.KycQueryData'
produces:
- application/json
responses:
"200":
description: Query processed (success/pending/failed)
schema:
allOf:
- $ref: '#/definitions/utils.RespStatus'
- properties:
data:
$ref: '#/definitions/service_kyc.KycQueryResponse'
type: object
"400":
description: Invalid UUID or input
schema:
allOf:
- $ref: '#/definitions/utils.RespStatus'
- properties:
data:
type: object
type: object
"403":
description: Unauthorized
schema:
allOf:
- $ref: '#/definitions/utils.RespStatus'
- properties:
data:
type: object
type: object
"500":
description: Internal Server Error
schema:
allOf:
- $ref: '#/definitions/utils.RespStatus'
- properties:
data:
type: object
type: object
security:
- ApiKeyAuth: []
summary: Query KYC Status
tags:
- KYC
/kyc/session:
post:
consumes:
- application/json
description: Initializes a KYC process (CNRid or Passport) and returns the status
or redirect URI.
parameters:
- description: KYC session data (Type and Base64 Identity)
in: body
name: payload
required: true
schema:
$ref: '#/definitions/service_kyc.KycSessionData'
produces:
- application/json
responses:
"200":
description: Session created successfully
schema:
allOf:
- $ref: '#/definitions/utils.RespStatus'
- properties:
data:
$ref: '#/definitions/service_kyc.KycSessionResponse'
type: object
"400":
description: Invalid input or decode failed
schema:
allOf:
- $ref: '#/definitions/utils.RespStatus'
- properties:
data:
type: object
type: object
"403":
description: Missing User ID
schema:
allOf:
- $ref: '#/definitions/utils.RespStatus'
- properties:
data:
type: object
type: object
"500":
description: Internal Server Error / KYC Service Error
schema:
allOf:
- $ref: '#/definitions/utils.RespStatus'
- properties:
data:
type: object
type: object
security:
- ApiKeyAuth: []
summary: Create KYC Session
tags:
- KYC
/user/info:
get:
consumes:

View File

@@ -14,17 +14,13 @@ import (
"github.com/spf13/viper"
)
func CNRidMD5AliEnc(kyc *KycInfo) (*AliCloudAuth, error) {
if kyc.Type != "Chinese" {
return nil, nil
}
func CNRidMD5AliEnc(kyc *CNRidInfo) (*AliCloudAuth, error) {
// MD5 Legal Name rule: First Chinese char md5enc, remaining plain, at least 2 Chinese chars
if len(kyc.CNRidInfo.LegalName) < 2 || utf8.RuneCountInString(kyc.CNRidInfo.LegalName) < 2 {
if len(kyc.LegalName) < 2 || utf8.RuneCountInString(kyc.LegalName) < 2 {
return nil, fmt.Errorf("input string must have at least 2 Chinese characters")
}
lnFirstRune, size := utf8.DecodeRuneInString(kyc.CNRidInfo.LegalName)
lnFirstRune, size := utf8.DecodeRuneInString(kyc.LegalName)
if lnFirstRune == utf8.RuneError {
return nil, fmt.Errorf("invalid first character")
}
@@ -33,18 +29,18 @@ func CNRidMD5AliEnc(kyc *KycInfo) (*AliCloudAuth, error) {
lnHash.Write([]byte(string(lnFirstRune)))
lnFirstHash := hex.EncodeToString(lnHash.Sum(nil))
lnRemaining := kyc.CNRidInfo.LegalName[size:]
lnRemaining := kyc.LegalName[size:]
ln := lnFirstHash + lnRemaining
// MD5 Resident Id rule: First 6 char plain, middle birthdate md5enc, last 4 char plain, at least 18 chars
if len(kyc.CNRidInfo.ResidentId) < 18 {
if len(kyc.ResidentId) < 18 {
return nil, fmt.Errorf("input string must have at least 18 characters")
}
ridPrefix := kyc.CNRidInfo.ResidentId[:6]
ridSuffix := kyc.CNRidInfo.ResidentId[len(kyc.CNRidInfo.ResidentId)-4:]
ridMiddle := kyc.CNRidInfo.ResidentId[6 : len(kyc.CNRidInfo.ResidentId)-4]
ridPrefix := kyc.ResidentId[:6]
ridSuffix := kyc.ResidentId[len(kyc.ResidentId)-4:]
ridMiddle := kyc.ResidentId[6 : len(kyc.ResidentId)-4]
ridHash := md5.New()
ridHash.Write([]byte(ridMiddle))

View File

@@ -8,7 +8,7 @@ import (
"github.com/spf13/viper"
)
func EncodeAES(kyc *KycInfo) (*string, error) {
func EncodeAES(kyc any) (*string, error) {
plainJson, err := json.Marshal(kyc)
if err != nil {
return nil, err
@@ -23,14 +23,14 @@ func EncodeAES(kyc *KycInfo) (*string, error) {
return &encrypted, nil
}
func DecodeAES(cipherStr string) (*KycInfo, error) {
func DecodeAES(cipherStr string) (any, error) {
aesKey := viper.GetString("secrets.kyc_info_key")
plainBytes, err := cryptography.AESCBCDecrypt(cipherStr, []byte(aesKey))
if err != nil {
return nil, err
}
var kycInfo KycInfo
var kycInfo any
if err := json.Unmarshal(plainBytes, &kycInfo); err != nil {
return nil, errors.New("[KYC] invalid decrypted json")
}

View File

@@ -2,6 +2,7 @@ package kyc
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
@@ -11,78 +12,80 @@ import (
"github.com/spf13/viper"
)
func CreateSession() (*PassportReaderSessionResponse, error) {
const (
StateCreated = "CREATED"
StateInitiated = "INITIATED"
StateFailed = "FAILED"
StateAborted = "ABORTED"
StateCompleted = "COMPLETED"
StateRejected = "REJECTED"
StateApproved = "APPROVED"
)
func doPassportRequest(ctx context.Context, method, path string, body any, target any) error {
publicKey := viper.GetString("kyc.passport_reader_public_key")
secret := viper.GetString("kyc.passport_reader_secret")
baseURL := "https://passportreader.app/api/v1"
apiURL := "https://passportreader.app/api/v1/session.create"
client := &http.Client{
Timeout: 10 * time.Second,
var bodyReader io.Reader
if body != nil {
jsonData, err := json.Marshal(body)
if err != nil {
return fmt.Errorf("marshal request failed: %w", err)
}
bodyReader = bytes.NewBuffer(jsonData)
}
req, err := http.NewRequest("POST", apiURL, nil)
req, err := http.NewRequestWithContext(ctx, method, baseURL+path, bodyReader)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.SetBasicAuth(publicKey, secret)
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("api returned error: %d, body: %s", resp.StatusCode, string(body))
}
var sessionResp PassportReaderSessionResponse
if err := json.NewDecoder(resp.Body).Decode(&sessionResp); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
return &sessionResp, nil
}
func GetSessionDetails(sessionID int) (*PassportReaderSessionDetailResponse, error) {
publicKey := viper.GetString("kyc.passport_reader_public_key")
secret := viper.GetString("kyc.passport_reader_secret")
apiURL := "https://passportreader.app/api/v1/session.get"
reqPayload := PassportReaderGetSessionRequest{ID: sessionID}
jsonData, err := json.Marshal(reqPayload)
if err != nil {
return nil, fmt.Errorf("failed to marshal request: %w", err)
}
req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonData))
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
return fmt.Errorf("create request failed: %w", err)
}
req.SetBasicAuth(publicKey, secret)
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
client := &http.Client{Timeout: 15 * time.Second}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
return fmt.Errorf("http request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("api error: %d, response: %s", resp.StatusCode, string(body))
respBody, _ := io.ReadAll(resp.Body)
return fmt.Errorf("api error: status %d, body %s", resp.StatusCode, string(respBody))
}
var detailResp PassportReaderSessionDetailResponse
if err := json.NewDecoder(resp.Body).Decode(&detailResp); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
if target != nil {
if err := json.NewDecoder(resp.Body).Decode(target); err != nil {
return fmt.Errorf("decode response failed: %w", err)
}
}
return &detailResp, nil
return nil
}
func CreateSession(ctx context.Context) (*PassportReaderSessionResponse, error) {
var resp PassportReaderSessionResponse
err := doPassportRequest(ctx, "POST", "/session.create", nil, &resp)
return &resp, err
}
func GetSessionState(ctx context.Context, sessionID int) (string, error) {
payload := PassportReaderGetSessionRequest{ID: sessionID}
var resp PassportReaderStateResponse
err := doPassportRequest(ctx, "POST", "/session.state", payload, &resp)
if err != nil {
return "", err
}
return resp.State, nil
}
func GetSessionDetails(ctx context.Context, sessionID int) (*PassportReaderSessionDetailResponse, error) {
payload := PassportReaderGetSessionRequest{ID: sessionID}
var resp PassportReaderSessionDetailResponse
err := doPassportRequest(ctx, "POST", "/session.get", payload, &resp)
return &resp, err
}

View File

@@ -1,17 +1,15 @@
package kyc
type KycInfo struct {
Type string `json:"type"` // cnrid/passport
CNRidInfo *CNRidInfo `json:"CNRodInfo"`
PassportInfo *PassportInfo `json:"passport_info"`
}
type CNRidInfo struct {
LegalName string `json:"legal_name"`
ResidentId string `json:"resident_id"`
}
type PassportInfo struct {
ID string `json:"id"`
}
type PassportResp struct {
GivenNames string `json:"given_names"`
Surname string `json:"surname"`
Nationality string `json:"nationality"`
@@ -36,6 +34,10 @@ type PassportReaderGetSessionRequest struct {
ID int `json:"id"`
}
type PassportReaderStateResponse struct {
State string `json:"state"`
}
type PassportReaderSessionDetailResponse struct {
State string `json:"state"`
GivenNames string `json:"given_names"`

View File

@@ -30,6 +30,45 @@ type CheckinResult struct {
}
func (self *EventServiceImpl) Checkin(payload *CheckinPayload) (result *CheckinResult) {
attendandeData, err := new(data.Attendance).
GetAttendanceByEventIdAndUserId(payload.Context, payload.Data.EventId, payload.UserId)
if err != nil {
result = &CheckinResult{
Common: shared.CommonResult{
HttpCode: 500,
Exception: new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceEvent).
SetEndpoint(exception.EndpointEventServiceCheckin).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorDatabase).
SetError(err).
Throw(payload.Context),
},
}
return
}
eventData, err := new(data.Event).
GetEventById(payload.Context, payload.Data.EventId)
if attendandeData.KycId == uuid.Nil && eventData.EnableKYC == true {
result = &CheckinResult{
Common: shared.CommonResult{
HttpCode: 400,
Exception: new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceEvent).
SetEndpoint(exception.EndpointEventServiceCheckin).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
SetError(err).
Throw(payload.Context),
},
}
return
}
attendance := &data.Attendance{UserId: payload.UserId}
code, err := attendance.GenCheckinCode(payload.Context, payload.Data.EventId)
if err != nil {

View File

@@ -12,6 +12,7 @@ import (
type EventJoinData struct {
EventId string `json:"event_id"`
KycId string `json:"kyc_id"`
UserId string `json:"user_id" swaggerignore:"true"`
Role string `json:"role" swaggerignore:"true"`
State string `json:"state" swaggerignore:"true"`
@@ -30,7 +31,6 @@ func (self *EventServiceImpl) JoinEvent(payload *EventJoinPayload) (result *Even
var err error
attendenceData := new(data.Attendance)
eventData := new(data.Event)
eventId, err := uuid.Parse(payload.Data.EventId)
if err != nil {
@@ -53,19 +53,41 @@ func (self *EventServiceImpl) JoinEvent(payload *EventJoinPayload) (result *Even
return
}
if !eventData.EndTime.Before(time.Now()) {
eventData, err := new(data.Event).GetEventById(payload.Context, eventId)
if err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceEvent).
SetEndpoint(exception.EndpointEventServiceJoin).
SetType(exception.TypeSpecific).
SetOriginal(exception.EventJoinEventInvalid).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorDatabase).
SetError(err).
Throw(payload.Context)
result = &EventJoinResult{
Common: shared.CommonResult{
HttpCode: 403,
HttpCode: 500,
Exception: exception,
},
}
return
}
if eventData.EnableKYC == true && payload.Data.KycId == "" {
exception := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceEvent).
SetEndpoint(exception.EndpointEventServiceJoin).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
SetError(nil).
Throw(payload.Context)
result = &EventJoinResult{
Common: shared.CommonResult{
HttpCode: 400,
Exception: exception,
},
}
@@ -94,8 +116,94 @@ func (self *EventServiceImpl) JoinEvent(payload *EventJoinPayload) (result *Even
return
}
attendenceData.SetEventId(eventId)
if eventData.EnableKYC == true && payload.Data.KycId != "" {
kycId, err := uuid.Parse(payload.Data.KycId)
if err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceEvent).
SetEndpoint(exception.EndpointEventServiceJoin).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorUuidParseFailed).
SetError(err).
Throw(payload.Context)
result = &EventJoinResult{
Common: shared.CommonResult{
HttpCode: 400,
Exception: exception,
},
}
return
}
kycData, err := new(data.Kyc).GetByKycId(payload.Context, &kycId)
if err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceEvent).
SetEndpoint(exception.EndpointEventServiceJoin).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorDatabase).
SetError(err).
Throw(payload.Context)
result = &EventJoinResult{
Common: shared.CommonResult{
HttpCode: 500,
Exception: exception,
},
}
return
}
if kycData.UserId != userId {
exception := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceEvent).
SetEndpoint(exception.EndpointEventServiceJoin).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
SetError(err).
Throw(payload.Context)
result = &EventJoinResult{
Common: shared.CommonResult{
HttpCode: 400,
Exception: exception,
},
}
return
}
attendenceData.SetKycId(kycData.KycId)
}
if !eventData.EndTime.Before(time.Now()) {
exception := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceEvent).
SetEndpoint(exception.EndpointEventServiceJoin).
SetType(exception.TypeSpecific).
SetOriginal(exception.EventJoinEventInvalid).
SetError(err).
Throw(payload.Context)
result = &EventJoinResult{
Common: shared.CommonResult{
HttpCode: 403,
Exception: exception,
},
}
return
}
attendenceData.SetUserId(userId)
attendenceData.SetEventId(eventId)
attendenceData.SetRole("notmal")
attendenceData.SetState("success")

View File

@@ -29,7 +29,7 @@ func (self *EventServiceImpl) ListEvents(payload *EventListPayload) (result *Eve
var offset string
if payload.Offset == nil || *payload.Offset == "" {
exc := new(exception.Builder).
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceEvent).
SetEndpoint(exception.EndpointEventServiceList).
@@ -41,7 +41,7 @@ func (self *EventServiceImpl) ListEvents(payload *EventListPayload) (result *Eve
return &EventListResult{
Common: shared.CommonResult{
HttpCode: 400,
Exception: exc,
Exception: exception,
},
Data: nil,
}

View File

@@ -0,0 +1,251 @@
package service_kyc
import (
"context"
"nixcn-cms/data"
"nixcn-cms/internal/exception"
"nixcn-cms/internal/kyc"
"nixcn-cms/service/shared"
"github.com/google/uuid"
)
type KycQueryData struct {
KycId string `json:"kyc_id"`
}
type KycQueryPayload struct {
Context context.Context
Data *KycQueryData
}
type KycQueryResponse struct {
Status string `json:"status"` // success | pending | failed
}
type KycQueryResult struct {
Common shared.CommonResult
Data *KycQueryResponse
}
func (self *KycServiceImpl) QueryKyc(payload *KycQueryPayload) (result *KycQueryResult) {
var err error
sessionState, err := kyc.GetSessionState(payload.Context, self.PassportReaderSessionId)
if err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceQuery).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
SetError(err).
Throw(payload.Context)
result = &KycQueryResult{
Common: shared.CommonResult{
HttpCode: 400,
Exception: exception,
},
Data: nil,
}
return
}
if sessionState == kyc.StateApproved {
sessionDetails, err := kyc.GetSessionDetails(payload.Context, self.PassportReaderSessionId)
if err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceQuery).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
SetError(err).
Throw(payload.Context)
result = &KycQueryResult{
Common: shared.CommonResult{
HttpCode: 400,
Exception: exception,
},
Data: nil,
}
return
}
var kycInfo = kyc.PassportResp{
GivenNames: sessionDetails.GivenNames,
Surname: sessionDetails.Surname,
Nationality: sessionDetails.Nationality,
DateOfBirth: sessionDetails.DateOfBirth,
DocumentType: sessionDetails.DocumentType,
DocumentNumber: sessionDetails.DocumentNumber,
ExpiryDate: sessionDetails.ExpiryDate,
}
if kycInfo.DocumentType != "PASSPORT" || kycInfo.DocumentNumber != self.PassportId {
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceQuery).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
SetError(err).
Throw(payload.Context)
result = &KycQueryResult{
Common: shared.CommonResult{
HttpCode: 400,
Exception: exception,
},
Data: nil,
}
return
}
encodedKycInfo, err := kyc.EncodeAES(kycInfo)
if err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceQuery).
SetType(exception.TypeSpecific).
SetOriginal(exception.KycQueryFailed).
SetError(err).
Throw(payload.Context)
result = &KycQueryResult{
Common: shared.CommonResult{
HttpCode: 500,
Exception: exception,
},
Data: nil,
}
return
}
kycId, err := uuid.Parse(payload.Data.KycId)
if err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceEvent).
SetEndpoint(exception.EndpointKycServiceQuery).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorUuidParseFailed).
SetError(err).
Throw(payload.Context)
result = &KycQueryResult{
Common: shared.CommonResult{
HttpCode: 400,
Exception: exception,
},
Data: nil,
}
return
}
var updates = map[string]any{
"kyc_info": kycInfo,
}
err = new(data.Kyc).
SetKycInfo(*encodedKycInfo).
UpdateByKycID(payload.Context, &kycId, updates)
if err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceQuery).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorDatabase).
SetError(err).
Throw(payload.Context)
result = &KycQueryResult{
Common: shared.CommonResult{
HttpCode: 500,
Exception: exception,
},
Data: nil,
}
return
}
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceQuery).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonSuccess).
SetError(nil).
Throw(payload.Context)
result = &KycQueryResult{
Common: shared.CommonResult{
HttpCode: 200,
Exception: exception,
},
Data: &KycQueryResponse{
Status: "success",
},
}
return
}
if sessionState == kyc.StateCreated || sessionState == kyc.StateInitiated || sessionState == kyc.StateCompleted {
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceQuery).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonSuccess).
SetError(nil).
Throw(payload.Context)
result = &KycQueryResult{
Common: shared.CommonResult{
HttpCode: 200,
Exception: exception,
},
Data: &KycQueryResponse{
Status: "pending",
},
}
return
}
if sessionState == kyc.StateFailed || sessionState == kyc.StateAborted || sessionState == kyc.StateRejected {
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceQuery).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonSuccess).
SetError(nil).
Throw(payload.Context)
result = &KycQueryResult{
Common: shared.CommonResult{
HttpCode: 200,
Exception: exception,
},
Data: &KycQueryResponse{
Status: "failed",
},
}
return
}
return
}

View File

@@ -0,0 +1,15 @@
package service_kyc
type KycService interface {
SessionKyc(*KycSessionPayload) *KycSessionResult
QueryKyc(*KycQueryPayload) *KycQueryResult
}
type KycServiceImpl struct {
PassportId string `json:"passport_id"`
PassportReaderSessionId int `json:"passport_reader_session_id"`
}
func NewKycService() KycService {
return &KycServiceImpl{}
}

View File

@@ -0,0 +1,400 @@
package service_kyc
import (
"context"
"encoding/base64"
"encoding/json"
"net/url"
"nixcn-cms/data"
"nixcn-cms/internal/exception"
"nixcn-cms/internal/kyc"
"nixcn-cms/service/shared"
"github.com/google/uuid"
)
// cnrid: {"legal_name":"", "resident_id":""}
// passport: {"id": ""}
type KycSessionData struct {
Type string `json:"type"` // cnrid | passport
Identity string `json:"identity"` // base64 json
UserId string `json:"user_id" swaggerignore:"true"`
}
type KycSessionPayload struct {
Context context.Context
Data *KycSessionData
}
type KycSessionResponse struct {
Status string `json:"status"` // success | processing
KycId *string `json:"kyc_id"`
RedirectUri *string `json:"redirect_uri"`
}
type KycSessionResult struct {
Common shared.CommonResult
Data *KycSessionResponse
}
func (self *KycServiceImpl) SessionKyc(payload *KycSessionPayload) (result *KycSessionResult) {
var err error
decodedIdentityByte, err := base64.StdEncoding.DecodeString(payload.Data.Identity)
if err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceSession).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorBase64DecodeFailed).
SetError(err).
Throw(payload.Context)
result = &KycSessionResult{
Common: shared.CommonResult{
HttpCode: 400,
Exception: exception,
},
Data: nil,
}
return
}
switch payload.Data.Type {
case "cnrid":
var info kyc.CNRidInfo
if err := json.Unmarshal(decodedIdentityByte, &info); err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceSession).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorJsonDecodeFailed).
SetError(err).
Throw(payload.Context)
result = &KycSessionResult{
Common: shared.CommonResult{
HttpCode: 400,
Exception: exception,
},
Data: nil,
}
return
}
kycInfo := kyc.CNRidInfo{
LegalName: info.LegalName,
ResidentId: info.ResidentId,
}
aliCloudAuth, err := kyc.CNRidMD5AliEnc(&kycInfo)
if err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceSession).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInternal).
SetError(err).
Throw(payload.Context)
result = &KycSessionResult{
Common: shared.CommonResult{
HttpCode: 500,
Exception: exception,
},
Data: nil,
}
return
}
kycResult, err := kyc.AliId2MetaVerify(aliCloudAuth)
if err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceSession).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInternal).
SetError(err).
Throw(payload.Context)
result = &KycSessionResult{
Common: shared.CommonResult{
HttpCode: 500,
Exception: exception,
},
Data: nil,
}
return
}
if *kycResult != "1" {
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceSession).
SetType(exception.TypeSpecific).
SetOriginal(exception.KycSessionFailed).
SetError(err).
Throw(payload.Context)
result = &KycSessionResult{
Common: shared.CommonResult{
HttpCode: 500,
Exception: exception,
},
Data: nil,
}
return
}
userId, err := uuid.Parse(payload.Data.UserId)
if err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceEvent).
SetEndpoint(exception.EndpointEventServiceJoin).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorUuidParseFailed).
SetError(err).
Throw(payload.Context)
result = &KycSessionResult{
Common: shared.CommonResult{
HttpCode: 400,
Exception: exception,
},
Data: nil,
}
return
}
encodedKycInfo, err := kyc.EncodeAES(kycInfo)
if err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceSession).
SetType(exception.TypeSpecific).
SetOriginal(exception.KycSessionFailed).
SetError(err).
Throw(payload.Context)
result = &KycSessionResult{
Common: shared.CommonResult{
HttpCode: 500,
Exception: exception,
},
Data: nil,
}
return
}
kycIdOrig, err := new(data.Kyc).
SetType("cnrid").
SetUserId(userId).
SetKycInfo(*encodedKycInfo).
Create(payload.Context)
if err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceSession).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorDatabase).
SetError(err).
Throw(payload.Context)
result = &KycSessionResult{
Common: shared.CommonResult{
HttpCode: 500,
Exception: exception,
},
Data: nil,
}
return
}
kycId := kycIdOrig.String()
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceSession).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonSuccess).
SetError(nil).
Throw(payload.Context)
result = &KycSessionResult{
Common: shared.CommonResult{
HttpCode: 200,
Exception: exception,
},
Data: &KycSessionResponse{
Status: "success",
KycId: &kycId,
RedirectUri: nil,
},
}
case "passport":
var info kyc.PassportInfo
if err := json.Unmarshal(decodedIdentityByte, &info); err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceSession).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorJsonDecodeFailed).
SetError(err).
Throw(payload.Context)
result = &KycSessionResult{
Common: shared.CommonResult{
HttpCode: 400,
Exception: exception,
},
Data: nil,
}
return
}
self.PassportId = info.ID
sessionResponse, err := kyc.CreateSession(payload.Context)
if err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceSession).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInternal).
SetError(err).
Throw(payload.Context)
result = &KycSessionResult{
Common: shared.CommonResult{
HttpCode: 500,
Exception: exception,
},
Data: nil,
}
return
}
self.PassportReaderSessionId = sessionResponse.ID
userId, err := uuid.Parse(payload.Data.UserId)
if err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceEvent).
SetEndpoint(exception.EndpointEventServiceJoin).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorUuidParseFailed).
SetError(err).
Throw(payload.Context)
result = &KycSessionResult{
Common: shared.CommonResult{
HttpCode: 400,
Exception: exception,
},
Data: nil,
}
return
}
kycIdOrig, err := new(data.Kyc).
SetType("passport").
SetUserId(userId).
Create(payload.Context)
if err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceSession).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorDatabase).
SetError(err).
Throw(payload.Context)
result = &KycSessionResult{
Common: shared.CommonResult{
HttpCode: 500,
Exception: exception,
},
Data: nil,
}
return
}
kycId := kycIdOrig.String()
kycBaseURL := "https://passportreader.app/open"
kycUrl, _ := url.Parse(kycBaseURL)
kycQuery := kycUrl.Query()
kycQuery.Set("token", sessionResponse.Token)
kycUrl.RawQuery = kycQuery.Encode()
redirectUri := kycUrl.String()
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceSession).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonSuccess).
SetError(nil).
Throw(payload.Context)
result = &KycSessionResult{
Common: shared.CommonResult{
HttpCode: 200,
Exception: exception,
},
Data: &KycSessionResponse{
Status: "processing",
KycId: &kycId,
RedirectUri: &redirectUri,
},
}
default:
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceKyc).
SetEndpoint(exception.EndpointKycServiceSession).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
SetError(err).
Throw(payload.Context)
result = &KycSessionResult{
Common: shared.CommonResult{
HttpCode: 400,
Exception: exception,
},
Data: nil,
}
return
}
return
}