| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551 |
- package service
- import (
- "context"
- "strings"
- "testing"
- "time"
- dbent "github.com/Wei-Shaw/sub2api/ent"
- "github.com/Wei-Shaw/sub2api/internal/payment"
- infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
- )
- func TestShouldUseAlipayMobilePrecreate(t *testing.T) {
- t.Parallel()
- enabled := &PaymentConfig{AlipayMobilePrecreateDeepLink: true}
- officialAlipay := &payment.InstanceSelection{ProviderKey: payment.TypeAlipay}
- tests := []struct {
- name string
- req CreateOrderRequest
- cfg *PaymentConfig
- sel *payment.InstanceSelection
- want bool
- }{
- {name: "mobile official alipay with switch", req: CreateOrderRequest{IsMobile: true}, cfg: enabled, sel: officialAlipay, want: true},
- {name: "desktop remains unchanged", req: CreateOrderRequest{IsMobile: false}, cfg: enabled, sel: officialAlipay, want: false},
- {name: "switch disabled keeps wap", req: CreateOrderRequest{IsMobile: true}, cfg: &PaymentConfig{}, sel: officialAlipay, want: false},
- {name: "other provider remains unchanged", req: CreateOrderRequest{IsMobile: true}, cfg: enabled, sel: &payment.InstanceSelection{ProviderKey: payment.TypeEasyPay}, want: false},
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- t.Parallel()
- if got := shouldUseAlipayMobilePrecreate(tt.req, tt.cfg, tt.sel); got != tt.want {
- t.Fatalf("shouldUseAlipayMobilePrecreate() = %v, want %v", got, tt.want)
- }
- })
- }
- }
- func TestIsOfficialAlipayProviderInstance(t *testing.T) {
- t.Parallel()
- tests := []struct {
- name string
- instance *dbent.PaymentProviderInstance
- want bool
- }{
- {name: "nil instance", instance: nil, want: false},
- {name: "official alipay", instance: &dbent.PaymentProviderInstance{ProviderKey: payment.TypeAlipay}, want: true},
- {name: "normalized official alipay", instance: &dbent.PaymentProviderInstance{ProviderKey: " ALIPAY "}, want: true},
- {name: "easypay alipay route", instance: &dbent.PaymentProviderInstance{ProviderKey: payment.TypeEasyPay}, want: false},
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- t.Parallel()
- if got := isOfficialAlipayProviderInstance(tt.instance); got != tt.want {
- t.Fatalf("isOfficialAlipayProviderInstance() = %v, want %v", got, tt.want)
- }
- })
- }
- }
- func TestBuildCreateOrderResponseDefaultsToOrderCreated(t *testing.T) {
- t.Parallel()
- expiresAt := time.Date(2026, 4, 16, 12, 0, 0, 0, time.UTC)
- resp := buildCreateOrderResponse(
- &dbent.PaymentOrder{
- ID: 42,
- Amount: 12.34,
- FeeRate: 0.03,
- ExpiresAt: expiresAt,
- OutTradeNo: "sub2_42",
- },
- CreateOrderRequest{PaymentType: payment.TypeWxpay},
- 12.71,
- &payment.InstanceSelection{PaymentMode: "qrcode"},
- &payment.CreatePaymentResponse{
- TradeNo: "sub2_42",
- QRCode: "weixin://wxpay/bizpayurl?pr=test",
- },
- payment.CreatePaymentResultOrderCreated,
- )
- if resp.ResultType != payment.CreatePaymentResultOrderCreated {
- t.Fatalf("result type = %q, want %q", resp.ResultType, payment.CreatePaymentResultOrderCreated)
- }
- if resp.OutTradeNo != "sub2_42" {
- t.Fatalf("out_trade_no = %q, want %q", resp.OutTradeNo, "sub2_42")
- }
- if resp.QRCode != "weixin://wxpay/bizpayurl?pr=test" {
- t.Fatalf("qr_code = %q, want %q", resp.QRCode, "weixin://wxpay/bizpayurl?pr=test")
- }
- if resp.JSAPI != nil || resp.JSAPIPayload != nil {
- t.Fatal("order_created response should not include jsapi payload")
- }
- if !resp.ExpiresAt.Equal(expiresAt) {
- t.Fatalf("expires_at = %v, want %v", resp.ExpiresAt, expiresAt)
- }
- }
- func TestBuildCreateOrderResponseCopiesJSAPIPayload(t *testing.T) {
- t.Parallel()
- jsapiPayload := &payment.WechatJSAPIPayload{
- AppID: "wx123",
- TimeStamp: "1712345678",
- NonceStr: "nonce-123",
- Package: "prepay_id=wx123",
- SignType: "RSA",
- PaySign: "signed-payload",
- }
- resp := buildCreateOrderResponse(
- &dbent.PaymentOrder{
- ID: 88,
- Amount: 66.88,
- FeeRate: 0.01,
- ExpiresAt: time.Date(2026, 4, 16, 13, 0, 0, 0, time.UTC),
- OutTradeNo: "sub2_88",
- },
- CreateOrderRequest{PaymentType: payment.TypeWxpay},
- 67.55,
- &payment.InstanceSelection{PaymentMode: "popup"},
- &payment.CreatePaymentResponse{
- TradeNo: "sub2_88",
- ResultType: payment.CreatePaymentResultJSAPIReady,
- JSAPI: jsapiPayload,
- },
- payment.CreatePaymentResultJSAPIReady,
- )
- if resp.ResultType != payment.CreatePaymentResultJSAPIReady {
- t.Fatalf("result type = %q, want %q", resp.ResultType, payment.CreatePaymentResultJSAPIReady)
- }
- if resp.JSAPI == nil || resp.JSAPIPayload == nil {
- t.Fatal("expected jsapi payload aliases to be populated")
- }
- if resp.JSAPI != jsapiPayload || resp.JSAPIPayload != jsapiPayload {
- t.Fatal("expected jsapi aliases to preserve the original pointer")
- }
- }
- func TestSanitizeCreatePaymentResponseDetailsRemovesNULBytes(t *testing.T) {
- t.Parallel()
- resp := &payment.CreatePaymentResponse{
- TradeNo: "trade\x00-no",
- PayURL: "https://pay.example.com/\x00checkout",
- QRCode: "wxp://payment-token\x00",
- ClientSecret: "secret\x00unchanged",
- }
- sanitizeCreatePaymentResponseDetails(resp)
- if strings.ContainsRune(resp.TradeNo, 0) {
- t.Fatalf("trade_no still contains NUL: %q", resp.TradeNo)
- }
- if strings.ContainsRune(resp.PayURL, 0) {
- t.Fatalf("pay_url still contains NUL: %q", resp.PayURL)
- }
- if strings.ContainsRune(resp.QRCode, 0) {
- t.Fatalf("qr_code still contains NUL: %q", resp.QRCode)
- }
- if resp.TradeNo != "trade-no" {
- t.Fatalf("trade_no = %q, want trade-no", resp.TradeNo)
- }
- if resp.PayURL != "https://pay.example.com/checkout" {
- t.Fatalf("pay_url = %q, want sanitized URL", resp.PayURL)
- }
- if resp.QRCode != "wxp://payment-token" {
- t.Fatalf("qr_code = %q, want sanitized QR code", resp.QRCode)
- }
- if resp.ClientSecret != "secret\x00unchanged" {
- t.Fatalf("client_secret = %q, should not be touched by payment detail sanitization", resp.ClientSecret)
- }
- }
- func TestValidateSelectedCreateOrderAmountCurrencyRejectsFractionalZeroDecimal(t *testing.T) {
- t.Parallel()
- err := validateSelectedCreateOrderAmountCurrency("100.50", &payment.InstanceSelection{
- ProviderKey: payment.TypeStripe,
- Config: map[string]string{"currency": "JPY"},
- })
- if err == nil {
- t.Fatal("expected fractional JPY amount to fail")
- }
- if appErr := infraerrors.FromError(err); appErr.Reason != "INVALID_AMOUNT" {
- t.Fatalf("reason = %q, want INVALID_AMOUNT", appErr.Reason)
- }
- }
- func TestCalculateCreateOrderPayAmountUsesCurrencyPrecision(t *testing.T) {
- t.Parallel()
- amountStr, amount, err := calculateCreateOrderPayAmount(100, 2.5, "JPY")
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if amountStr != "103" || amount != 103 {
- t.Fatalf("JPY pay amount = (%q, %v), want (103, 103)", amountStr, amount)
- }
- amountStr, amount, err = calculateCreateOrderPayAmount(12.345, 1, "KWD")
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if amountStr != "12.469" || amount != 12.469 {
- t.Fatalf("KWD pay amount = (%q, %v), want (12.469, 12.469)", amountStr, amount)
- }
- }
- func TestCalculateCreateOrderPayAmountForSubscriptionConvertsCNYPriceWhenRateConfigured(t *testing.T) {
- t.Parallel()
- amountStr, amount, err := calculateCreateOrderPayAmountForOrderType(9.99, 0, "CNY", payment.OrderTypeSubscription, 7.15)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if amountStr != "71.43" || amount != 71.43 {
- t.Fatalf("subscription CNY pay amount = (%q, %v), want (71.43, 71.43)", amountStr, amount)
- }
- }
- func TestCalculateCreateOrderPayAmountForSubscriptionAppliesFeeAfterCNYConversion(t *testing.T) {
- t.Parallel()
- amountStr, amount, err := calculateCreateOrderPayAmountForOrderType(9.99, 2.5, "CNY", payment.OrderTypeSubscription, 7.15)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if amountStr != "73.22" || amount != 73.22 {
- t.Fatalf("subscription CNY pay amount with fee = (%q, %v), want (73.22, 73.22)", amountStr, amount)
- }
- }
- func TestCalculateCreateOrderPayAmountForSubscriptionKeepsNonCNYPrice(t *testing.T) {
- t.Parallel()
- amountStr, amount, err := calculateCreateOrderPayAmountForOrderType(9.99, 0, "USD", payment.OrderTypeSubscription, 7.15)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if amountStr != "9.99" || amount != 9.99 {
- t.Fatalf("subscription USD pay amount = (%q, %v), want (9.99, 9.99)", amountStr, amount)
- }
- }
- // 换算是 opt-in:未配置汇率(rate=0)时,CNY 订阅保持 price 直付的存量行为。
- // 该测试锁住存量部署升级后行为不变的兼容承诺。
- func TestCalculateCreateOrderPayAmountForSubscriptionKeepsDirectPriceWhenRateDisabled(t *testing.T) {
- t.Parallel()
- amountStr, amount, err := calculateCreateOrderPayAmountForOrderType(9.99, 0, "CNY", payment.OrderTypeSubscription, 0)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if amountStr != "9.99" || amount != 9.99 {
- t.Fatalf("subscription CNY pay amount without rate = (%q, %v), want (9.99, 9.99)", amountStr, amount)
- }
- }
- // 汇率只作用于订阅订单,余额充值订单不受影响。
- func TestCalculateCreateOrderPayAmountForBalanceIgnoresSubscriptionRate(t *testing.T) {
- t.Parallel()
- amountStr, amount, err := calculateCreateOrderPayAmountForOrderType(50, 0, "CNY", payment.OrderTypeBalance, 7.15)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if amountStr != "50.00" || amount != 50 {
- t.Fatalf("balance CNY pay amount = (%q, %v), want (50.00, 50)", amountStr, amount)
- }
- }
- func TestCalculateCreditedBalanceStillUsesRechargeMultiplier(t *testing.T) {
- t.Parallel()
- got := calculateCreditedBalance(10, 0.14)
- if got != 1.4 {
- t.Fatalf("credited balance = %v, want 1.4", got)
- }
- got = calculateCreditedBalance(5, 10)
- if got != 50 {
- t.Fatalf("credited balance = %v, want 50", got)
- }
- }
- func TestCalculateCreateOrderPayAmountRejectsFractionalZeroDecimal(t *testing.T) {
- t.Parallel()
- _, _, err := calculateCreateOrderPayAmount(100.5, 0, "JPY")
- if err == nil {
- t.Fatal("expected fractional JPY amount to fail")
- }
- if appErr := infraerrors.FromError(err); appErr.Reason != "INVALID_AMOUNT" {
- t.Fatalf("reason = %q, want INVALID_AMOUNT", appErr.Reason)
- }
- }
- func TestComputeValidityDaysSupportsSingularAndPluralUnits(t *testing.T) {
- t.Parallel()
- tests := []struct {
- name string
- days int
- unit string
- want int
- }{
- {name: "days", days: 1, unit: "days", want: 1},
- {name: "week", days: 1, unit: "week", want: 7},
- {name: "weeks", days: 2, unit: "weeks", want: 14},
- {name: "month", days: 1, unit: "month", want: 30},
- {name: "months", days: 1, unit: "months", want: 30},
- }
- for _, tt := range tests {
- tt := tt
- t.Run(tt.name, func(t *testing.T) {
- t.Parallel()
- if got := psComputeValidityDays(tt.days, tt.unit); got != tt.want {
- t.Fatalf("psComputeValidityDays(%d, %q) = %d, want %d", tt.days, tt.unit, got, tt.want)
- }
- })
- }
- }
- func TestBuildPaymentSubjectAppliesAffixToSubscriptionPlanProductName(t *testing.T) {
- t.Parallel()
- svc := &PaymentService{}
- cfg := &PaymentConfig{
- ProductNamePrefix: "PRE",
- ProductNameSuffix: "SUF",
- }
- plan := &dbent.SubscriptionPlan{
- Name: "Pro Monthly",
- ProductName: "Claude Pro",
- }
- got := svc.buildPaymentSubject(plan, 0, cfg, nil)
- if got != "PRE Claude Pro SUF" {
- t.Fatalf("buildPaymentSubject() = %q, want %q", got, "PRE Claude Pro SUF")
- }
- }
- func TestBuildPaymentSubjectAppliesAffixToSubscriptionPlanDefaultName(t *testing.T) {
- t.Parallel()
- svc := &PaymentService{}
- cfg := &PaymentConfig{
- ProductNamePrefix: "PRE",
- ProductNameSuffix: "SUF",
- }
- plan := &dbent.SubscriptionPlan{Name: "Team Monthly"}
- got := svc.buildPaymentSubject(plan, 0, cfg, nil)
- if got != "PRE Sub2API Subscription Team Monthly SUF" {
- t.Fatalf("buildPaymentSubject() = %q, want %q", got, "PRE Sub2API Subscription Team Monthly SUF")
- }
- }
- func TestMaybeBuildWeChatOAuthRequiredResponse(t *testing.T) {
- t.Setenv("PAYMENT_RESUME_SIGNING_KEY", "0123456789abcdef0123456789abcdef")
- svc := newWeChatPaymentOAuthTestService(map[string]string{
- SettingKeyWeChatConnectEnabled: "true",
- SettingKeyWeChatConnectAppID: "wx123456",
- SettingKeyWeChatConnectAppSecret: "wechat-secret",
- SettingKeyWeChatConnectMode: "mp",
- SettingKeyWeChatConnectScopes: "snsapi_base",
- SettingKeyWeChatConnectRedirectURL: "https://api.example.com/api/v1/auth/oauth/wechat/callback",
- SettingKeyWeChatConnectFrontendRedirectURL: "/auth/wechat/callback",
- })
- resp, err := svc.maybeBuildWeChatOAuthRequiredResponse(context.Background(), CreateOrderRequest{
- Amount: 12.5,
- PaymentType: payment.TypeWxpay,
- IsWeChatBrowser: true,
- SrcURL: "https://merchant.example/payment?from=wechat",
- OrderType: payment.OrderTypeBalance,
- }, 12.5, 12.88, 0.03)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if resp == nil {
- t.Fatal("expected oauth_required response, got nil")
- }
- if resp.ResultType != payment.CreatePaymentResultOAuthRequired {
- t.Fatalf("result type = %q, want %q", resp.ResultType, payment.CreatePaymentResultOAuthRequired)
- }
- if resp.OAuth == nil {
- t.Fatal("expected oauth payload, got nil")
- }
- if resp.OAuth.AppID != "wx123456" {
- t.Fatalf("appid = %q, want %q", resp.OAuth.AppID, "wx123456")
- }
- if resp.OAuth.Scope != "snsapi_base" {
- t.Fatalf("scope = %q, want %q", resp.OAuth.Scope, "snsapi_base")
- }
- if resp.OAuth.RedirectURL != "/auth/wechat/payment/callback" {
- t.Fatalf("redirect_url = %q, want %q", resp.OAuth.RedirectURL, "/auth/wechat/payment/callback")
- }
- if resp.OAuth.AuthorizeURL != "/api/v1/auth/oauth/wechat/payment/start?amount=12.5&order_type=balance&payment_type=wxpay&redirect=%2Fpurchase%3Ffrom%3Dwechat&scope=snsapi_base" {
- t.Fatalf("authorize_url = %q", resp.OAuth.AuthorizeURL)
- }
- }
- func TestMaybeBuildWeChatOAuthRequiredResponseRequiresMPConfigInWeChat(t *testing.T) {
- t.Parallel()
- svc := newWeChatPaymentOAuthTestService(nil)
- resp, err := svc.maybeBuildWeChatOAuthRequiredResponse(context.Background(), CreateOrderRequest{
- Amount: 12.5,
- PaymentType: payment.TypeWxpay,
- IsWeChatBrowser: true,
- SrcURL: "https://merchant.example/payment?from=wechat",
- OrderType: payment.OrderTypeBalance,
- }, 12.5, 12.88, 0.03)
- if resp != nil {
- t.Fatalf("expected nil response, got %+v", resp)
- }
- if err == nil {
- t.Fatal("expected error, got nil")
- }
- appErr := infraerrors.FromError(err)
- if appErr.Reason != "WECHAT_PAYMENT_MP_NOT_CONFIGURED" {
- t.Fatalf("reason = %q, want %q", appErr.Reason, "WECHAT_PAYMENT_MP_NOT_CONFIGURED")
- }
- }
- func TestMaybeBuildWeChatOAuthRequiredResponseRequiresResumeSigningKey(t *testing.T) {
- t.Parallel()
- svc := &PaymentService{
- configService: &PaymentConfigService{
- settingRepo: &paymentConfigSettingRepoStub{values: map[string]string{
- SettingKeyWeChatConnectEnabled: "true",
- SettingKeyWeChatConnectAppID: "wx123456",
- SettingKeyWeChatConnectAppSecret: "wechat-secret",
- SettingKeyWeChatConnectMode: "mp",
- SettingKeyWeChatConnectScopes: "snsapi_base",
- SettingKeyWeChatConnectRedirectURL: "https://api.example.com/api/v1/auth/oauth/wechat/callback",
- SettingKeyWeChatConnectFrontendRedirectURL: "/auth/wechat/callback",
- }},
- // Intentionally missing payment resume signing key.
- encryptionKey: nil,
- },
- }
- resp, err := svc.maybeBuildWeChatOAuthRequiredResponse(context.Background(), CreateOrderRequest{
- Amount: 12.5,
- PaymentType: payment.TypeWxpay,
- IsWeChatBrowser: true,
- SrcURL: "https://merchant.example/payment?from=wechat",
- OrderType: payment.OrderTypeBalance,
- }, 12.5, 12.88, 0.03)
- if resp != nil {
- t.Fatalf("expected nil response, got %+v", resp)
- }
- if err == nil {
- t.Fatal("expected error, got nil")
- }
- appErr := infraerrors.FromError(err)
- if appErr.Reason != "PAYMENT_RESUME_NOT_CONFIGURED" {
- t.Fatalf("reason = %q, want %q", appErr.Reason, "PAYMENT_RESUME_NOT_CONFIGURED")
- }
- }
- func TestMaybeBuildWeChatOAuthRequiredResponseFallsBackToConfiguredLegacySigningKey(t *testing.T) {
- svc := &PaymentService{
- configService: &PaymentConfigService{
- settingRepo: &paymentConfigSettingRepoStub{values: map[string]string{
- SettingKeyWeChatConnectEnabled: "true",
- SettingKeyWeChatConnectAppID: "wx123456",
- SettingKeyWeChatConnectAppSecret: "wechat-secret",
- SettingKeyWeChatConnectMode: "mp",
- SettingKeyWeChatConnectScopes: "snsapi_base",
- SettingKeyWeChatConnectRedirectURL: "https://api.example.com/api/v1/auth/oauth/wechat/callback",
- SettingKeyWeChatConnectFrontendRedirectURL: "/auth/wechat/callback",
- }},
- // Legacy stable signing key remains available for no-config upgrade compatibility.
- encryptionKey: []byte("0123456789abcdef0123456789abcdef"),
- },
- }
- resp, err := svc.maybeBuildWeChatOAuthRequiredResponse(context.Background(), CreateOrderRequest{
- Amount: 12.5,
- PaymentType: payment.TypeWxpay,
- IsWeChatBrowser: true,
- SrcURL: "https://merchant.example/payment?from=wechat",
- OrderType: payment.OrderTypeBalance,
- }, 12.5, 12.88, 0.03)
- if err != nil {
- t.Fatalf("expected nil error, got %v", err)
- }
- if resp == nil {
- t.Fatal("expected oauth-required response, got nil")
- }
- if resp.ResultType != payment.CreatePaymentResultOAuthRequired {
- t.Fatalf("result type = %q, want %q", resp.ResultType, payment.CreatePaymentResultOAuthRequired)
- }
- if resp.OAuth == nil || strings.TrimSpace(resp.OAuth.AuthorizeURL) == "" {
- t.Fatalf("expected oauth redirect payload, got %+v", resp.OAuth)
- }
- }
- func TestMaybeBuildWeChatOAuthRequiredResponseForSelectionSkipsEasyPayProvider(t *testing.T) {
- svc := newWeChatPaymentOAuthTestService(map[string]string{
- SettingKeyWeChatConnectEnabled: "true",
- SettingKeyWeChatConnectAppID: "wx123456",
- SettingKeyWeChatConnectAppSecret: "wechat-secret",
- SettingKeyWeChatConnectMode: "mp",
- SettingKeyWeChatConnectScopes: "snsapi_base",
- SettingKeyWeChatConnectRedirectURL: "https://api.example.com/api/v1/auth/oauth/wechat/callback",
- SettingKeyWeChatConnectFrontendRedirectURL: "/auth/wechat/callback",
- })
- resp, err := svc.maybeBuildWeChatOAuthRequiredResponseForSelection(context.Background(), CreateOrderRequest{
- Amount: 12.5,
- PaymentType: payment.TypeWxpay,
- IsWeChatBrowser: true,
- OrderType: payment.OrderTypeBalance,
- }, 12.5, 12.88, 0.03, &payment.InstanceSelection{
- ProviderKey: payment.TypeEasyPay,
- })
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if resp != nil {
- t.Fatalf("expected nil response, got %+v", resp)
- }
- }
- func newWeChatPaymentOAuthTestService(values map[string]string) *PaymentService {
- return &PaymentService{
- configService: &PaymentConfigService{
- settingRepo: &paymentConfigSettingRepoStub{values: values},
- encryptionKey: []byte("0123456789abcdef0123456789abcdef"),
- },
- }
- }
|