Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
561405ab
Commit
561405ab
authored
Apr 21, 2026
by
IanShaw027
Browse files
feat: add payment order provider snapshots
parent
440536a9
Changes
14
Show whitespace changes
Inline
Side-by-side
backend/ent/migrate/schema.go
View file @
561405ab
...
...
@@ -655,6 +655,7 @@ var (
{
Name
:
"subscription_days"
,
Type
:
field
.
TypeInt
,
Nullable
:
true
},
{
Name
:
"provider_instance_id"
,
Type
:
field
.
TypeString
,
Nullable
:
true
,
Size
:
64
},
{
Name
:
"provider_key"
,
Type
:
field
.
TypeString
,
Nullable
:
true
,
Size
:
30
},
{
Name
:
"provider_snapshot"
,
Type
:
field
.
TypeJSON
,
Nullable
:
true
,
SchemaType
:
map
[
string
]
string
{
"postgres"
:
"jsonb"
}},
{
Name
:
"status"
,
Type
:
field
.
TypeString
,
Size
:
30
,
Default
:
"PENDING"
},
{
Name
:
"refund_amount"
,
Type
:
field
.
TypeFloat64
,
Default
:
0
,
SchemaType
:
map
[
string
]
string
{
"postgres"
:
"decimal(20,2)"
}},
{
Name
:
"refund_reason"
,
Type
:
field
.
TypeString
,
Nullable
:
true
,
SchemaType
:
map
[
string
]
string
{
"postgres"
:
"text"
}},
...
...
@@ -683,7 +684,7 @@ var (
ForeignKeys
:
[]
*
schema
.
ForeignKey
{
{
Symbol
:
"payment_orders_users_payment_orders"
,
Columns
:
[]
*
schema
.
Column
{
PaymentOrdersColumns
[
3
8
]},
Columns
:
[]
*
schema
.
Column
{
PaymentOrdersColumns
[
3
9
]},
RefColumns
:
[]
*
schema
.
Column
{
UsersColumns
[
0
]},
OnDelete
:
schema
.
NoAction
,
},
...
...
@@ -697,32 +698,32 @@ var (
{
Name
:
"paymentorder_user_id"
,
Unique
:
false
,
Columns
:
[]
*
schema
.
Column
{
PaymentOrdersColumns
[
3
8
]},
Columns
:
[]
*
schema
.
Column
{
PaymentOrdersColumns
[
3
9
]},
},
{
Name
:
"paymentorder_status"
,
Unique
:
false
,
Columns
:
[]
*
schema
.
Column
{
PaymentOrdersColumns
[
2
0
]},
Columns
:
[]
*
schema
.
Column
{
PaymentOrdersColumns
[
2
1
]},
},
{
Name
:
"paymentorder_expires_at"
,
Unique
:
false
,
Columns
:
[]
*
schema
.
Column
{
PaymentOrdersColumns
[
2
8
]},
Columns
:
[]
*
schema
.
Column
{
PaymentOrdersColumns
[
2
9
]},
},
{
Name
:
"paymentorder_created_at"
,
Unique
:
false
,
Columns
:
[]
*
schema
.
Column
{
PaymentOrdersColumns
[
3
6
]},
Columns
:
[]
*
schema
.
Column
{
PaymentOrdersColumns
[
3
7
]},
},
{
Name
:
"paymentorder_paid_at"
,
Unique
:
false
,
Columns
:
[]
*
schema
.
Column
{
PaymentOrdersColumns
[
29
]},
Columns
:
[]
*
schema
.
Column
{
PaymentOrdersColumns
[
30
]},
},
{
Name
:
"paymentorder_payment_type_paid_at"
,
Unique
:
false
,
Columns
:
[]
*
schema
.
Column
{
PaymentOrdersColumns
[
9
],
PaymentOrdersColumns
[
29
]},
Columns
:
[]
*
schema
.
Column
{
PaymentOrdersColumns
[
9
],
PaymentOrdersColumns
[
30
]},
},
{
Name
:
"paymentorder_order_type"
,
...
...
backend/ent/mutation.go
View file @
561405ab
...
...
@@ -15386,6 +15386,7 @@ type PaymentOrderMutation struct {
addsubscription_days *int
provider_instance_id *string
provider_key *string
provider_snapshot *map[string]interface{}
status *string
refund_amount *float64
addrefund_amount *float64
...
...
@@ -16471,6 +16472,55 @@ func (m *PaymentOrderMutation) ResetProviderKey() {
delete(m.clearedFields, paymentorder.FieldProviderKey)
}
// SetProviderSnapshot sets the "provider_snapshot" field.
func (m *PaymentOrderMutation) SetProviderSnapshot(value map[string]interface{}) {
m.provider_snapshot = &value
}
// ProviderSnapshot returns the value of the "provider_snapshot" field in the mutation.
func (m *PaymentOrderMutation) ProviderSnapshot() (r map[string]interface{}, exists bool) {
v := m.provider_snapshot
if v == nil {
return
}
return *v, true
}
// OldProviderSnapshot returns the old "provider_snapshot" field's value of the PaymentOrder entity.
// If the PaymentOrder object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *PaymentOrderMutation) OldProviderSnapshot(ctx context.Context) (v map[string]interface{}, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldProviderSnapshot is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldProviderSnapshot requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldProviderSnapshot: %w", err)
}
return oldValue.ProviderSnapshot, nil
}
// ClearProviderSnapshot clears the value of the "provider_snapshot" field.
func (m *PaymentOrderMutation) ClearProviderSnapshot() {
m.provider_snapshot = nil
m.clearedFields[paymentorder.FieldProviderSnapshot] = struct{}{}
}
// ProviderSnapshotCleared returns if the "provider_snapshot" field was cleared in this mutation.
func (m *PaymentOrderMutation) ProviderSnapshotCleared() bool {
_, ok := m.clearedFields[paymentorder.FieldProviderSnapshot]
return ok
}
// ResetProviderSnapshot resets all changes to the "provider_snapshot" field.
func (m *PaymentOrderMutation) ResetProviderSnapshot() {
m.provider_snapshot = nil
delete(m.clearedFields, paymentorder.FieldProviderSnapshot)
}
// SetStatus sets the "status" field.
func (m *PaymentOrderMutation) SetStatus(s string) {
m.status = &s
...
...
@@ -17330,7 +17380,7 @@ func (m *PaymentOrderMutation) Type() string {
// order to get all numeric fields that were incremented/decremented, call
// AddedFields().
func (m *PaymentOrderMutation) Fields() []string {
fields := make([]string, 0, 3
8
)
fields := make([]string, 0, 3
9
)
if m.user != nil {
fields = append(fields, paymentorder.FieldUserID)
}
...
...
@@ -17391,6 +17441,9 @@ func (m *PaymentOrderMutation) Fields() []string {
if m.provider_key != nil {
fields = append(fields, paymentorder.FieldProviderKey)
}
if m.provider_snapshot != nil {
fields = append(fields, paymentorder.FieldProviderSnapshot)
}
if m.status != nil {
fields = append(fields, paymentorder.FieldStatus)
}
...
...
@@ -17493,6 +17546,8 @@ func (m *PaymentOrderMutation) Field(name string) (ent.Value, bool) {
return m.ProviderInstanceID()
case paymentorder.FieldProviderKey:
return m.ProviderKey()
case paymentorder.FieldProviderSnapshot:
return m.ProviderSnapshot()
case paymentorder.FieldStatus:
return m.Status()
case paymentorder.FieldRefundAmount:
...
...
@@ -17578,6 +17633,8 @@ func (m *PaymentOrderMutation) OldField(ctx context.Context, name string) (ent.V
return m.OldProviderInstanceID(ctx)
case paymentorder.FieldProviderKey:
return m.OldProviderKey(ctx)
case paymentorder.FieldProviderSnapshot:
return m.OldProviderSnapshot(ctx)
case paymentorder.FieldStatus:
return m.OldStatus(ctx)
case paymentorder.FieldRefundAmount:
...
...
@@ -17763,6 +17820,13 @@ func (m *PaymentOrderMutation) SetField(name string, value ent.Value) error {
}
m.SetProviderKey(v)
return nil
case paymentorder.FieldProviderSnapshot:
v, ok := value.(map[string]interface{})
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetProviderSnapshot(v)
return nil
case paymentorder.FieldStatus:
v, ok := value.(string)
if !ok {
...
...
@@ -18033,6 +18097,9 @@ func (m *PaymentOrderMutation) ClearedFields() []string {
if m.FieldCleared(paymentorder.FieldProviderKey) {
fields = append(fields, paymentorder.FieldProviderKey)
}
if m.FieldCleared(paymentorder.FieldProviderSnapshot) {
fields = append(fields, paymentorder.FieldProviderSnapshot)
}
if m.FieldCleared(paymentorder.FieldRefundReason) {
fields = append(fields, paymentorder.FieldRefundReason)
}
...
...
@@ -18104,6 +18171,9 @@ func (m *PaymentOrderMutation) ClearField(name string) error {
case paymentorder.FieldProviderKey:
m.ClearProviderKey()
return nil
case paymentorder.FieldProviderSnapshot:
m.ClearProviderSnapshot()
return nil
case paymentorder.FieldRefundReason:
m.ClearRefundReason()
return nil
...
...
@@ -18202,6 +18272,9 @@ func (m *PaymentOrderMutation) ResetField(name string) error {
case paymentorder.FieldProviderKey:
m.ResetProviderKey()
return nil
case paymentorder.FieldProviderSnapshot:
m.ResetProviderSnapshot()
return nil
case paymentorder.FieldStatus:
m.ResetStatus()
return nil
backend/ent/paymentorder.go
View file @
561405ab
...
...
@@ -3,6 +3,7 @@
package
ent
import
(
"encoding/json"
"fmt"
"strings"
"time"
...
...
@@ -58,6 +59,8 @@ type PaymentOrder struct {
ProviderInstanceID
*
string
`json:"provider_instance_id,omitempty"`
// ProviderKey holds the value of the "provider_key" field.
ProviderKey
*
string
`json:"provider_key,omitempty"`
// ProviderSnapshot holds the value of the "provider_snapshot" field.
ProviderSnapshot
map
[
string
]
interface
{}
`json:"provider_snapshot,omitempty"`
// Status holds the value of the "status" field.
Status
string
`json:"status,omitempty"`
// RefundAmount holds the value of the "refund_amount" field.
...
...
@@ -125,6 +128,8 @@ func (*PaymentOrder) scanValues(columns []string) ([]any, error) {
values
:=
make
([]
any
,
len
(
columns
))
for
i
:=
range
columns
{
switch
columns
[
i
]
{
case
paymentorder
.
FieldProviderSnapshot
:
values
[
i
]
=
new
([]
byte
)
case
paymentorder
.
FieldForceRefund
:
values
[
i
]
=
new
(
sql
.
NullBool
)
case
paymentorder
.
FieldAmount
,
paymentorder
.
FieldPayAmount
,
paymentorder
.
FieldFeeRate
,
paymentorder
.
FieldRefundAmount
:
...
...
@@ -285,6 +290,14 @@ func (_m *PaymentOrder) assignValues(columns []string, values []any) error {
_m
.
ProviderKey
=
new
(
string
)
*
_m
.
ProviderKey
=
value
.
String
}
case
paymentorder
.
FieldProviderSnapshot
:
if
value
,
ok
:=
values
[
i
]
.
(
*
[]
byte
);
!
ok
{
return
fmt
.
Errorf
(
"unexpected type %T for field provider_snapshot"
,
values
[
i
])
}
else
if
value
!=
nil
&&
len
(
*
value
)
>
0
{
if
err
:=
json
.
Unmarshal
(
*
value
,
&
_m
.
ProviderSnapshot
);
err
!=
nil
{
return
fmt
.
Errorf
(
"unmarshal field provider_snapshot: %w"
,
err
)
}
}
case
paymentorder
.
FieldStatus
:
if
value
,
ok
:=
values
[
i
]
.
(
*
sql
.
NullString
);
!
ok
{
return
fmt
.
Errorf
(
"unexpected type %T for field status"
,
values
[
i
])
...
...
@@ -522,6 +535,9 @@ func (_m *PaymentOrder) String() string {
builder
.
WriteString
(
*
v
)
}
builder
.
WriteString
(
", "
)
builder
.
WriteString
(
"provider_snapshot="
)
builder
.
WriteString
(
fmt
.
Sprintf
(
"%v"
,
_m
.
ProviderSnapshot
))
builder
.
WriteString
(
", "
)
builder
.
WriteString
(
"status="
)
builder
.
WriteString
(
_m
.
Status
)
builder
.
WriteString
(
", "
)
...
...
backend/ent/paymentorder/paymentorder.go
View file @
561405ab
...
...
@@ -54,6 +54,8 @@ const (
FieldProviderInstanceID
=
"provider_instance_id"
// FieldProviderKey holds the string denoting the provider_key field in the database.
FieldProviderKey
=
"provider_key"
// FieldProviderSnapshot holds the string denoting the provider_snapshot field in the database.
FieldProviderSnapshot
=
"provider_snapshot"
// FieldStatus holds the string denoting the status field in the database.
FieldStatus
=
"status"
// FieldRefundAmount holds the string denoting the refund_amount field in the database.
...
...
@@ -126,6 +128,7 @@ var Columns = []string{
FieldSubscriptionDays
,
FieldProviderInstanceID
,
FieldProviderKey
,
FieldProviderSnapshot
,
FieldStatus
,
FieldRefundAmount
,
FieldRefundReason
,
...
...
backend/ent/paymentorder/where.go
View file @
561405ab
...
...
@@ -1440,6 +1440,16 @@ func ProviderKeyContainsFold(v string) predicate.PaymentOrder {
return
predicate
.
PaymentOrder
(
sql
.
FieldContainsFold
(
FieldProviderKey
,
v
))
}
// ProviderSnapshotIsNil applies the IsNil predicate on the "provider_snapshot" field.
func
ProviderSnapshotIsNil
()
predicate
.
PaymentOrder
{
return
predicate
.
PaymentOrder
(
sql
.
FieldIsNull
(
FieldProviderSnapshot
))
}
// ProviderSnapshotNotNil applies the NotNil predicate on the "provider_snapshot" field.
func
ProviderSnapshotNotNil
()
predicate
.
PaymentOrder
{
return
predicate
.
PaymentOrder
(
sql
.
FieldNotNull
(
FieldProviderSnapshot
))
}
// StatusEQ applies the EQ predicate on the "status" field.
func
StatusEQ
(
v
string
)
predicate
.
PaymentOrder
{
return
predicate
.
PaymentOrder
(
sql
.
FieldEQ
(
FieldStatus
,
v
))
...
...
backend/ent/paymentorder_create.go
View file @
561405ab
...
...
@@ -239,6 +239,12 @@ func (_c *PaymentOrderCreate) SetNillableProviderKey(v *string) *PaymentOrderCre
return
_c
}
// SetProviderSnapshot sets the "provider_snapshot" field.
func
(
_c
*
PaymentOrderCreate
)
SetProviderSnapshot
(
v
map
[
string
]
interface
{})
*
PaymentOrderCreate
{
_c
.
mutation
.
SetProviderSnapshot
(
v
)
return
_c
}
// SetStatus sets the "status" field.
func
(
_c
*
PaymentOrderCreate
)
SetStatus
(
v
string
)
*
PaymentOrderCreate
{
_c
.
mutation
.
SetStatus
(
v
)
...
...
@@ -771,6 +777,10 @@ func (_c *PaymentOrderCreate) createSpec() (*PaymentOrder, *sqlgraph.CreateSpec)
_spec
.
SetField
(
paymentorder
.
FieldProviderKey
,
field
.
TypeString
,
value
)
_node
.
ProviderKey
=
&
value
}
if
value
,
ok
:=
_c
.
mutation
.
ProviderSnapshot
();
ok
{
_spec
.
SetField
(
paymentorder
.
FieldProviderSnapshot
,
field
.
TypeJSON
,
value
)
_node
.
ProviderSnapshot
=
value
}
if
value
,
ok
:=
_c
.
mutation
.
Status
();
ok
{
_spec
.
SetField
(
paymentorder
.
FieldStatus
,
field
.
TypeString
,
value
)
_node
.
Status
=
value
...
...
@@ -1242,6 +1252,24 @@ func (u *PaymentOrderUpsert) ClearProviderKey() *PaymentOrderUpsert {
return
u
}
// SetProviderSnapshot sets the "provider_snapshot" field.
func
(
u
*
PaymentOrderUpsert
)
SetProviderSnapshot
(
v
map
[
string
]
interface
{})
*
PaymentOrderUpsert
{
u
.
Set
(
paymentorder
.
FieldProviderSnapshot
,
v
)
return
u
}
// UpdateProviderSnapshot sets the "provider_snapshot" field to the value that was provided on create.
func
(
u
*
PaymentOrderUpsert
)
UpdateProviderSnapshot
()
*
PaymentOrderUpsert
{
u
.
SetExcluded
(
paymentorder
.
FieldProviderSnapshot
)
return
u
}
// ClearProviderSnapshot clears the value of the "provider_snapshot" field.
func
(
u
*
PaymentOrderUpsert
)
ClearProviderSnapshot
()
*
PaymentOrderUpsert
{
u
.
SetNull
(
paymentorder
.
FieldProviderSnapshot
)
return
u
}
// SetStatus sets the "status" field.
func
(
u
*
PaymentOrderUpsert
)
SetStatus
(
v
string
)
*
PaymentOrderUpsert
{
u
.
Set
(
paymentorder
.
FieldStatus
,
v
)
...
...
@@ -1942,6 +1970,27 @@ func (u *PaymentOrderUpsertOne) ClearProviderKey() *PaymentOrderUpsertOne {
})
}
// SetProviderSnapshot sets the "provider_snapshot" field.
func
(
u
*
PaymentOrderUpsertOne
)
SetProviderSnapshot
(
v
map
[
string
]
interface
{})
*
PaymentOrderUpsertOne
{
return
u
.
Update
(
func
(
s
*
PaymentOrderUpsert
)
{
s
.
SetProviderSnapshot
(
v
)
})
}
// UpdateProviderSnapshot sets the "provider_snapshot" field to the value that was provided on create.
func
(
u
*
PaymentOrderUpsertOne
)
UpdateProviderSnapshot
()
*
PaymentOrderUpsertOne
{
return
u
.
Update
(
func
(
s
*
PaymentOrderUpsert
)
{
s
.
UpdateProviderSnapshot
()
})
}
// ClearProviderSnapshot clears the value of the "provider_snapshot" field.
func
(
u
*
PaymentOrderUpsertOne
)
ClearProviderSnapshot
()
*
PaymentOrderUpsertOne
{
return
u
.
Update
(
func
(
s
*
PaymentOrderUpsert
)
{
s
.
ClearProviderSnapshot
()
})
}
// SetStatus sets the "status" field.
func
(
u
*
PaymentOrderUpsertOne
)
SetStatus
(
v
string
)
*
PaymentOrderUpsertOne
{
return
u
.
Update
(
func
(
s
*
PaymentOrderUpsert
)
{
...
...
@@ -2853,6 +2902,27 @@ func (u *PaymentOrderUpsertBulk) ClearProviderKey() *PaymentOrderUpsertBulk {
})
}
// SetProviderSnapshot sets the "provider_snapshot" field.
func
(
u
*
PaymentOrderUpsertBulk
)
SetProviderSnapshot
(
v
map
[
string
]
interface
{})
*
PaymentOrderUpsertBulk
{
return
u
.
Update
(
func
(
s
*
PaymentOrderUpsert
)
{
s
.
SetProviderSnapshot
(
v
)
})
}
// UpdateProviderSnapshot sets the "provider_snapshot" field to the value that was provided on create.
func
(
u
*
PaymentOrderUpsertBulk
)
UpdateProviderSnapshot
()
*
PaymentOrderUpsertBulk
{
return
u
.
Update
(
func
(
s
*
PaymentOrderUpsert
)
{
s
.
UpdateProviderSnapshot
()
})
}
// ClearProviderSnapshot clears the value of the "provider_snapshot" field.
func
(
u
*
PaymentOrderUpsertBulk
)
ClearProviderSnapshot
()
*
PaymentOrderUpsertBulk
{
return
u
.
Update
(
func
(
s
*
PaymentOrderUpsert
)
{
s
.
ClearProviderSnapshot
()
})
}
// SetStatus sets the "status" field.
func
(
u
*
PaymentOrderUpsertBulk
)
SetStatus
(
v
string
)
*
PaymentOrderUpsertBulk
{
return
u
.
Update
(
func
(
s
*
PaymentOrderUpsert
)
{
...
...
backend/ent/paymentorder_update.go
View file @
561405ab
...
...
@@ -405,6 +405,18 @@ func (_u *PaymentOrderUpdate) ClearProviderKey() *PaymentOrderUpdate {
return
_u
}
// SetProviderSnapshot sets the "provider_snapshot" field.
func
(
_u
*
PaymentOrderUpdate
)
SetProviderSnapshot
(
v
map
[
string
]
interface
{})
*
PaymentOrderUpdate
{
_u
.
mutation
.
SetProviderSnapshot
(
v
)
return
_u
}
// ClearProviderSnapshot clears the value of the "provider_snapshot" field.
func
(
_u
*
PaymentOrderUpdate
)
ClearProviderSnapshot
()
*
PaymentOrderUpdate
{
_u
.
mutation
.
ClearProviderSnapshot
()
return
_u
}
// SetStatus sets the "status" field.
func
(
_u
*
PaymentOrderUpdate
)
SetStatus
(
v
string
)
*
PaymentOrderUpdate
{
_u
.
mutation
.
SetStatus
(
v
)
...
...
@@ -941,6 +953,12 @@ func (_u *PaymentOrderUpdate) sqlSave(ctx context.Context) (_node int, err error
if
_u
.
mutation
.
ProviderKeyCleared
()
{
_spec
.
ClearField
(
paymentorder
.
FieldProviderKey
,
field
.
TypeString
)
}
if
value
,
ok
:=
_u
.
mutation
.
ProviderSnapshot
();
ok
{
_spec
.
SetField
(
paymentorder
.
FieldProviderSnapshot
,
field
.
TypeJSON
,
value
)
}
if
_u
.
mutation
.
ProviderSnapshotCleared
()
{
_spec
.
ClearField
(
paymentorder
.
FieldProviderSnapshot
,
field
.
TypeJSON
)
}
if
value
,
ok
:=
_u
.
mutation
.
Status
();
ok
{
_spec
.
SetField
(
paymentorder
.
FieldStatus
,
field
.
TypeString
,
value
)
}
...
...
@@ -1450,6 +1468,18 @@ func (_u *PaymentOrderUpdateOne) ClearProviderKey() *PaymentOrderUpdateOne {
return
_u
}
// SetProviderSnapshot sets the "provider_snapshot" field.
func
(
_u
*
PaymentOrderUpdateOne
)
SetProviderSnapshot
(
v
map
[
string
]
interface
{})
*
PaymentOrderUpdateOne
{
_u
.
mutation
.
SetProviderSnapshot
(
v
)
return
_u
}
// ClearProviderSnapshot clears the value of the "provider_snapshot" field.
func
(
_u
*
PaymentOrderUpdateOne
)
ClearProviderSnapshot
()
*
PaymentOrderUpdateOne
{
_u
.
mutation
.
ClearProviderSnapshot
()
return
_u
}
// SetStatus sets the "status" field.
func
(
_u
*
PaymentOrderUpdateOne
)
SetStatus
(
v
string
)
*
PaymentOrderUpdateOne
{
_u
.
mutation
.
SetStatus
(
v
)
...
...
@@ -2016,6 +2046,12 @@ func (_u *PaymentOrderUpdateOne) sqlSave(ctx context.Context) (_node *PaymentOrd
if
_u
.
mutation
.
ProviderKeyCleared
()
{
_spec
.
ClearField
(
paymentorder
.
FieldProviderKey
,
field
.
TypeString
)
}
if
value
,
ok
:=
_u
.
mutation
.
ProviderSnapshot
();
ok
{
_spec
.
SetField
(
paymentorder
.
FieldProviderSnapshot
,
field
.
TypeJSON
,
value
)
}
if
_u
.
mutation
.
ProviderSnapshotCleared
()
{
_spec
.
ClearField
(
paymentorder
.
FieldProviderSnapshot
,
field
.
TypeJSON
)
}
if
value
,
ok
:=
_u
.
mutation
.
Status
();
ok
{
_spec
.
SetField
(
paymentorder
.
FieldStatus
,
field
.
TypeString
,
value
)
}
...
...
backend/ent/runtime/runtime.go
View file @
561405ab
...
...
@@ -728,37 +728,37 @@ func init() {
// paymentorder.ProviderKeyValidator is a validator for the "provider_key" field. It is called by the builders before save.
paymentorder
.
ProviderKeyValidator
=
paymentorderDescProviderKey
.
Validators
[
0
]
.
(
func
(
string
)
error
)
// paymentorderDescStatus is the schema descriptor for status field.
paymentorderDescStatus
:=
paymentorderFields
[
2
0
]
.
Descriptor
()
paymentorderDescStatus
:=
paymentorderFields
[
2
1
]
.
Descriptor
()
// paymentorder.DefaultStatus holds the default value on creation for the status field.
paymentorder
.
DefaultStatus
=
paymentorderDescStatus
.
Default
.
(
string
)
// paymentorder.StatusValidator is a validator for the "status" field. It is called by the builders before save.
paymentorder
.
StatusValidator
=
paymentorderDescStatus
.
Validators
[
0
]
.
(
func
(
string
)
error
)
// paymentorderDescRefundAmount is the schema descriptor for refund_amount field.
paymentorderDescRefundAmount
:=
paymentorderFields
[
2
1
]
.
Descriptor
()
paymentorderDescRefundAmount
:=
paymentorderFields
[
2
2
]
.
Descriptor
()
// paymentorder.DefaultRefundAmount holds the default value on creation for the refund_amount field.
paymentorder
.
DefaultRefundAmount
=
paymentorderDescRefundAmount
.
Default
.
(
float64
)
// paymentorderDescForceRefund is the schema descriptor for force_refund field.
paymentorderDescForceRefund
:=
paymentorderFields
[
2
4
]
.
Descriptor
()
paymentorderDescForceRefund
:=
paymentorderFields
[
2
5
]
.
Descriptor
()
// paymentorder.DefaultForceRefund holds the default value on creation for the force_refund field.
paymentorder
.
DefaultForceRefund
=
paymentorderDescForceRefund
.
Default
.
(
bool
)
// paymentorderDescRefundRequestedBy is the schema descriptor for refund_requested_by field.
paymentorderDescRefundRequestedBy
:=
paymentorderFields
[
2
7
]
.
Descriptor
()
paymentorderDescRefundRequestedBy
:=
paymentorderFields
[
2
8
]
.
Descriptor
()
// paymentorder.RefundRequestedByValidator is a validator for the "refund_requested_by" field. It is called by the builders before save.
paymentorder
.
RefundRequestedByValidator
=
paymentorderDescRefundRequestedBy
.
Validators
[
0
]
.
(
func
(
string
)
error
)
// paymentorderDescClientIP is the schema descriptor for client_ip field.
paymentorderDescClientIP
:=
paymentorderFields
[
3
3
]
.
Descriptor
()
paymentorderDescClientIP
:=
paymentorderFields
[
3
4
]
.
Descriptor
()
// paymentorder.ClientIPValidator is a validator for the "client_ip" field. It is called by the builders before save.
paymentorder
.
ClientIPValidator
=
paymentorderDescClientIP
.
Validators
[
0
]
.
(
func
(
string
)
error
)
// paymentorderDescSrcHost is the schema descriptor for src_host field.
paymentorderDescSrcHost
:=
paymentorderFields
[
3
4
]
.
Descriptor
()
paymentorderDescSrcHost
:=
paymentorderFields
[
3
5
]
.
Descriptor
()
// paymentorder.SrcHostValidator is a validator for the "src_host" field. It is called by the builders before save.
paymentorder
.
SrcHostValidator
=
paymentorderDescSrcHost
.
Validators
[
0
]
.
(
func
(
string
)
error
)
// paymentorderDescCreatedAt is the schema descriptor for created_at field.
paymentorderDescCreatedAt
:=
paymentorderFields
[
3
6
]
.
Descriptor
()
paymentorderDescCreatedAt
:=
paymentorderFields
[
3
7
]
.
Descriptor
()
// paymentorder.DefaultCreatedAt holds the default value on creation for the created_at field.
paymentorder
.
DefaultCreatedAt
=
paymentorderDescCreatedAt
.
Default
.
(
func
()
time
.
Time
)
// paymentorderDescUpdatedAt is the schema descriptor for updated_at field.
paymentorderDescUpdatedAt
:=
paymentorderFields
[
3
7
]
.
Descriptor
()
paymentorderDescUpdatedAt
:=
paymentorderFields
[
3
8
]
.
Descriptor
()
// paymentorder.DefaultUpdatedAt holds the default value on creation for the updated_at field.
paymentorder
.
DefaultUpdatedAt
=
paymentorderDescUpdatedAt
.
Default
.
(
func
()
time
.
Time
)
// paymentorder.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
...
...
backend/ent/schema/payment_order.go
View file @
561405ab
...
...
@@ -95,6 +95,9 @@ func (PaymentOrder) Fields() []ent.Field {
Optional
()
.
Nillable
()
.
MaxLen
(
30
),
field
.
JSON
(
"provider_snapshot"
,
map
[
string
]
any
{})
.
Optional
()
.
SchemaType
(
map
[
string
]
string
{
dialect
.
Postgres
:
"jsonb"
}),
// 状态
field
.
String
(
"status"
)
.
...
...
backend/internal/handler/admin/payment_handler.go
View file @
561405ab
...
...
@@ -3,6 +3,7 @@ package admin
import
(
"strconv"
dbent
"github.com/Wei-Shaw/sub2api/ent"
"github.com/Wei-Shaw/sub2api/internal/pkg/response"
"github.com/Wei-Shaw/sub2api/internal/service"
...
...
@@ -66,7 +67,7 @@ func (h *PaymentHandler) ListOrders(c *gin.Context) {
response
.
ErrorFrom
(
c
,
err
)
return
}
response
.
Paginated
(
c
,
orders
,
int64
(
total
),
page
,
pageSize
)
response
.
Paginated
(
c
,
sanitizeAdminPaymentOrdersForResponse
(
orders
)
,
int64
(
total
),
page
,
pageSize
)
}
// GetOrderDetail returns detailed information about a single order.
...
...
@@ -82,7 +83,7 @@ func (h *PaymentHandler) GetOrderDetail(c *gin.Context) {
return
}
auditLogs
,
_
:=
h
.
paymentService
.
GetOrderAuditLogs
(
c
.
Request
.
Context
(),
orderID
)
response
.
Success
(
c
,
gin
.
H
{
"order"
:
order
,
"auditLogs"
:
auditLogs
})
response
.
Success
(
c
,
gin
.
H
{
"order"
:
sanitizeAdminPaymentOrderForResponse
(
order
)
,
"auditLogs"
:
auditLogs
})
}
// CancelOrder cancels a pending order (admin).
...
...
@@ -114,6 +115,26 @@ func (h *PaymentHandler) RetryFulfillment(c *gin.Context) {
response
.
Success
(
c
,
gin
.
H
{
"message"
:
"fulfillment retried"
})
}
func
sanitizeAdminPaymentOrdersForResponse
(
orders
[]
*
dbent
.
PaymentOrder
)
[]
*
dbent
.
PaymentOrder
{
if
len
(
orders
)
==
0
{
return
orders
}
out
:=
make
([]
*
dbent
.
PaymentOrder
,
0
,
len
(
orders
))
for
_
,
order
:=
range
orders
{
out
=
append
(
out
,
sanitizeAdminPaymentOrderForResponse
(
order
))
}
return
out
}
func
sanitizeAdminPaymentOrderForResponse
(
order
*
dbent
.
PaymentOrder
)
*
dbent
.
PaymentOrder
{
if
order
==
nil
{
return
nil
}
cloned
:=
*
order
cloned
.
ProviderSnapshot
=
nil
return
&
cloned
}
// AdminProcessRefundRequest is the request body for admin refund processing.
type
AdminProcessRefundRequest
struct
{
Amount
float64
`json:"amount"`
...
...
backend/internal/handler/payment_handler.go
View file @
561405ab
...
...
@@ -6,6 +6,7 @@ import (
"strconv"
"strings"
dbent
"github.com/Wei-Shaw/sub2api/ent"
"github.com/Wei-Shaw/sub2api/internal/payment"
infraerrors
"github.com/Wei-Shaw/sub2api/internal/pkg/errors"
"github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
...
...
@@ -327,7 +328,7 @@ func (h *PaymentHandler) GetMyOrders(c *gin.Context) {
response
.
ErrorFrom
(
c
,
err
)
return
}
response
.
Paginated
(
c
,
orders
,
int64
(
total
),
page
,
pageSize
)
response
.
Paginated
(
c
,
sanitizePaymentOrdersForResponse
(
orders
)
,
int64
(
total
),
page
,
pageSize
)
}
// GetOrder returns a single order for the authenticated user.
...
...
@@ -349,7 +350,7 @@ func (h *PaymentHandler) GetOrder(c *gin.Context) {
response
.
ErrorFrom
(
c
,
err
)
return
}
response
.
Success
(
c
,
order
)
response
.
Success
(
c
,
sanitizePaymentOrderForResponse
(
order
)
)
}
// CancelOrder cancels a pending order for the authenticated user.
...
...
@@ -445,7 +446,7 @@ func (h *PaymentHandler) VerifyOrder(c *gin.Context) {
response
.
ErrorFrom
(
c
,
err
)
return
}
response
.
Success
(
c
,
order
)
response
.
Success
(
c
,
sanitizePaymentOrderForResponse
(
order
)
)
}
// PublicOrderResult is the limited order info returned by the public verify endpoint.
...
...
@@ -523,6 +524,26 @@ func isMobile(c *gin.Context) bool {
return
false
}
func
sanitizePaymentOrdersForResponse
(
orders
[]
*
dbent
.
PaymentOrder
)
[]
*
dbent
.
PaymentOrder
{
if
len
(
orders
)
==
0
{
return
orders
}
out
:=
make
([]
*
dbent
.
PaymentOrder
,
0
,
len
(
orders
))
for
_
,
order
:=
range
orders
{
out
=
append
(
out
,
sanitizePaymentOrderForResponse
(
order
))
}
return
out
}
func
sanitizePaymentOrderForResponse
(
order
*
dbent
.
PaymentOrder
)
*
dbent
.
PaymentOrder
{
if
order
==
nil
{
return
nil
}
cloned
:=
*
order
cloned
.
ProviderSnapshot
=
nil
return
&
cloned
}
func
isWeChatBrowser
(
c
*
gin
.
Context
)
bool
{
return
strings
.
Contains
(
strings
.
ToLower
(
c
.
GetHeader
(
"User-Agent"
)),
"micromessenger"
)
}
backend/internal/service/payment_order.go
View file @
561405ab
...
...
@@ -73,7 +73,7 @@ func (s *PaymentService) CreateOrder(ctx context.Context, req CreateOrderRequest
if
oauthResp
!=
nil
{
return
oauthResp
,
nil
}
order
,
err
:=
s
.
createOrderInTx
(
ctx
,
req
,
user
,
plan
,
cfg
,
orderAmount
,
limitAmount
,
feeRate
,
payAmount
)
order
,
err
:=
s
.
createOrderInTx
(
ctx
,
req
,
user
,
plan
,
cfg
,
orderAmount
,
limitAmount
,
feeRate
,
payAmount
,
sel
)
if
err
!=
nil
{
return
nil
,
err
}
...
...
@@ -122,7 +122,7 @@ func (s *PaymentService) validateSubOrder(ctx context.Context, req CreateOrderRe
return
plan
,
nil
}
func
(
s
*
PaymentService
)
createOrderInTx
(
ctx
context
.
Context
,
req
CreateOrderRequest
,
user
*
User
,
plan
*
dbent
.
SubscriptionPlan
,
cfg
*
PaymentConfig
,
orderAmount
,
limitAmount
,
feeRate
,
payAmount
float64
)
(
*
dbent
.
PaymentOrder
,
error
)
{
func
(
s
*
PaymentService
)
createOrderInTx
(
ctx
context
.
Context
,
req
CreateOrderRequest
,
user
*
User
,
plan
*
dbent
.
SubscriptionPlan
,
cfg
*
PaymentConfig
,
orderAmount
,
limitAmount
,
feeRate
,
payAmount
float64
,
sel
*
payment
.
InstanceSelection
)
(
*
dbent
.
PaymentOrder
,
error
)
{
tx
,
err
:=
s
.
entClient
.
Tx
(
ctx
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"begin transaction: %w"
,
err
)
...
...
@@ -139,6 +139,13 @@ func (s *PaymentService) createOrderInTx(ctx context.Context, req CreateOrderReq
tm
=
defaultOrderTimeoutMin
}
exp
:=
time
.
Now
()
.
Add
(
time
.
Duration
(
tm
)
*
time
.
Minute
)
providerSnapshot
:=
buildPaymentOrderProviderSnapshot
(
sel
)
selectedInstanceID
:=
""
selectedProviderKey
:=
""
if
sel
!=
nil
{
selectedInstanceID
=
strings
.
TrimSpace
(
sel
.
InstanceID
)
selectedProviderKey
=
strings
.
TrimSpace
(
sel
.
ProviderKey
)
}
b
:=
tx
.
PaymentOrder
.
Create
()
.
SetUserID
(
req
.
UserID
)
.
SetUserEmail
(
user
.
Email
)
.
...
...
@@ -159,6 +166,15 @@ func (s *PaymentService) createOrderInTx(ctx context.Context, req CreateOrderReq
if
req
.
SrcURL
!=
""
{
b
.
SetSrcURL
(
req
.
SrcURL
)
}
if
selectedInstanceID
!=
""
{
b
.
SetProviderInstanceID
(
selectedInstanceID
)
}
if
selectedProviderKey
!=
""
{
b
.
SetProviderKey
(
selectedProviderKey
)
}
if
providerSnapshot
!=
nil
{
b
.
SetProviderSnapshot
(
providerSnapshot
)
}
if
plan
!=
nil
{
b
.
SetPlanID
(
plan
.
ID
)
.
SetSubscriptionGroupID
(
plan
.
GroupID
)
.
SetSubscriptionDays
(
psComputeValidityDays
(
plan
.
ValidityDays
,
plan
.
ValidityUnit
))
}
...
...
@@ -192,6 +208,35 @@ func (s *PaymentService) checkPendingLimit(ctx context.Context, tx *dbent.Tx, us
return
nil
}
func
buildPaymentOrderProviderSnapshot
(
sel
*
payment
.
InstanceSelection
)
map
[
string
]
any
{
if
sel
==
nil
{
return
nil
}
snapshot
:=
map
[
string
]
any
{}
snapshot
[
"schema_version"
]
=
1
instanceID
:=
strings
.
TrimSpace
(
sel
.
InstanceID
)
if
instanceID
!=
""
{
snapshot
[
"provider_instance_id"
]
=
instanceID
}
providerKey
:=
strings
.
TrimSpace
(
sel
.
ProviderKey
)
if
providerKey
!=
""
{
snapshot
[
"provider_key"
]
=
providerKey
}
paymentMode
:=
strings
.
TrimSpace
(
sel
.
PaymentMode
)
if
paymentMode
!=
""
{
snapshot
[
"payment_mode"
]
=
paymentMode
}
if
len
(
snapshot
)
==
1
{
return
nil
}
return
snapshot
}
func
(
s
*
PaymentService
)
checkDailyLimit
(
ctx
context
.
Context
,
tx
*
dbent
.
Tx
,
userID
int64
,
amount
,
limit
float64
)
error
{
if
limit
<=
0
{
return
nil
...
...
backend/internal/service/payment_order_provider_snapshot_test.go
0 → 100644
View file @
561405ab
//go:build unit
package
service
import
(
"context"
"strconv"
"testing"
"github.com/Wei-Shaw/sub2api/internal/payment"
"github.com/stretchr/testify/require"
)
func
TestBuildPaymentOrderProviderSnapshot_ExcludesSensitiveConfig
(
t
*
testing
.
T
)
{
t
.
Parallel
()
sel
:=
&
payment
.
InstanceSelection
{
InstanceID
:
"12"
,
ProviderKey
:
payment
.
TypeWxpay
,
SupportedTypes
:
"wxpay,wxpay_direct"
,
PaymentMode
:
"popup"
,
Config
:
map
[
string
]
string
{
"privateKey"
:
"secret"
,
"apiV3Key"
:
"secret-v3"
,
"appId"
:
"wx-app-id"
,
},
}
snapshot
:=
buildPaymentOrderProviderSnapshot
(
sel
)
require
.
Equal
(
t
,
map
[
string
]
any
{
"schema_version"
:
1
,
"provider_instance_id"
:
"12"
,
"provider_key"
:
payment
.
TypeWxpay
,
"payment_mode"
:
"popup"
,
},
snapshot
)
require
.
NotContains
(
t
,
snapshot
,
"config"
)
require
.
NotContains
(
t
,
snapshot
,
"privateKey"
)
require
.
NotContains
(
t
,
snapshot
,
"apiV3Key"
)
require
.
NotContains
(
t
,
snapshot
,
"supported_types"
)
require
.
NotContains
(
t
,
snapshot
,
"instance_name"
)
}
func
TestCreateOrderInTx_WritesProviderSnapshot
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
client
:=
newPaymentConfigServiceTestClient
(
t
)
user
,
err
:=
client
.
User
.
Create
()
.
SetEmail
(
"snapshot@example.com"
)
.
SetPasswordHash
(
"hash"
)
.
SetUsername
(
"snapshot-user"
)
.
Save
(
ctx
)
require
.
NoError
(
t
,
err
)
instance
,
err
:=
client
.
PaymentProviderInstance
.
Create
()
.
SetProviderKey
(
payment
.
TypeAlipay
)
.
SetName
(
"Primary Alipay"
)
.
SetConfig
(
`{"secretKey":"do-not-copy"}`
)
.
SetSupportedTypes
(
"alipay,alipay_direct"
)
.
SetPaymentMode
(
"redirect"
)
.
SetEnabled
(
true
)
.
Save
(
ctx
)
require
.
NoError
(
t
,
err
)
svc
:=
&
PaymentService
{
entClient
:
client
}
order
,
err
:=
svc
.
createOrderInTx
(
ctx
,
CreateOrderRequest
{
UserID
:
user
.
ID
,
PaymentType
:
payment
.
TypeAlipay
,
OrderType
:
payment
.
OrderTypeBalance
,
ClientIP
:
"127.0.0.1"
,
SrcHost
:
"app.example.com"
,
},
&
User
{
ID
:
user
.
ID
,
Email
:
user
.
Email
,
Username
:
user
.
Username
,
},
nil
,
&
PaymentConfig
{
MaxPendingOrders
:
3
,
OrderTimeoutMin
:
30
,
},
88
,
88
,
0
,
88
,
&
payment
.
InstanceSelection
{
InstanceID
:
strconv
.
FormatInt
(
instance
.
ID
,
10
),
ProviderKey
:
payment
.
TypeAlipay
,
SupportedTypes
:
"alipay,alipay_direct"
,
PaymentMode
:
"redirect"
,
Config
:
map
[
string
]
string
{
"secretKey"
:
"do-not-copy"
,
},
},
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
strconv
.
FormatInt
(
instance
.
ID
,
10
),
valueOrEmpty
(
order
.
ProviderInstanceID
))
require
.
Equal
(
t
,
payment
.
TypeAlipay
,
valueOrEmpty
(
order
.
ProviderKey
))
require
.
Equal
(
t
,
float64
(
1
),
order
.
ProviderSnapshot
[
"schema_version"
])
require
.
Equal
(
t
,
strconv
.
FormatInt
(
instance
.
ID
,
10
),
order
.
ProviderSnapshot
[
"provider_instance_id"
])
require
.
Equal
(
t
,
payment
.
TypeAlipay
,
order
.
ProviderSnapshot
[
"provider_key"
])
require
.
Equal
(
t
,
"redirect"
,
order
.
ProviderSnapshot
[
"payment_mode"
])
require
.
NotContains
(
t
,
order
.
ProviderSnapshot
,
"config"
)
require
.
NotContains
(
t
,
order
.
ProviderSnapshot
,
"secretKey"
)
require
.
NotContains
(
t
,
order
.
ProviderSnapshot
,
"supported_types"
)
require
.
NotContains
(
t
,
order
.
ProviderSnapshot
,
"instance_name"
)
}
func
valueOrEmpty
(
v
*
string
)
string
{
if
v
==
nil
{
return
""
}
return
*
v
}
backend/migrations/117_add_payment_order_provider_snapshot.sql
0 → 100644
View file @
561405ab
ALTER
TABLE
payment_orders
ADD
COLUMN
IF
NOT
EXISTS
provider_snapshot
JSONB
;
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment