|
@@ -0,0 +1,394 @@ |
|
|
|
|
|
package pay |
|
|
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
|
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/md" |
|
|
|
|
|
zhios_pay_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/utils" |
|
|
|
|
|
"encoding/json" |
|
|
|
|
|
"errors" |
|
|
|
|
|
"fmt" |
|
|
|
|
|
"github.com/iGoogle-ink/gopay" |
|
|
|
|
|
"github.com/iGoogle-ink/gopay/alipay" |
|
|
|
|
|
"github.com/iGoogle-ink/gopay/pkg/util" |
|
|
|
|
|
"github.com/iGoogle-ink/gopay/pkg/xhttp" |
|
|
|
|
|
"github.com/iGoogle-ink/gopay/pkg/xlog" |
|
|
|
|
|
"time" |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
const ( |
|
|
|
|
|
baseUrl = "https://openapi.alipay.com/gateway.do" |
|
|
|
|
|
sandboxBaseUrl = "https://openapi.alipaydev.com/gateway.do" |
|
|
|
|
|
baseUrlUtf8 = "https://openapi.alipay.com/gateway.do?charset=utf-8" |
|
|
|
|
|
sandboxBaseUrlUtf8 = "https://openapi.alipaydev.com/gateway.do?charset=utf-8" |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
func Comm(args map[string]string) *alipay.Client { |
|
|
|
|
|
paySet := AlipayCofing(args) |
|
|
|
|
|
if args["private_key"] == "" || args["app_id"] == "" { |
|
|
|
|
|
return nil |
|
|
|
|
|
} |
|
|
|
|
|
//初始化支付宝客户端 |
|
|
|
|
|
// appID 是在支付宝申请的APPID |
|
|
|
|
|
// priKey 是支付宝私钥 |
|
|
|
|
|
// code 支付宝授权码 |
|
|
|
|
|
client := alipay.NewClient(args["app_id"], args["private_key"], true) |
|
|
|
|
|
client.DebugSwitch = gopay.DebugOn |
|
|
|
|
|
//判断密钥的类型 |
|
|
|
|
|
rsa_type := alipay.RSA2 |
|
|
|
|
|
pkcs_type := alipay.PKCS1 |
|
|
|
|
|
if args["rsa"] == "1" { |
|
|
|
|
|
rsa_type = alipay.RSA |
|
|
|
|
|
} |
|
|
|
|
|
if args["pkcs"] == "1" { |
|
|
|
|
|
pkcs_type = alipay.PKCS8 |
|
|
|
|
|
} |
|
|
|
|
|
if paySet.PayAliUseType == "1" { |
|
|
|
|
|
rsa_type = alipay.RSA2 |
|
|
|
|
|
pkcs_type = alipay.PKCS8 |
|
|
|
|
|
} |
|
|
|
|
|
//配置公共参数 |
|
|
|
|
|
client.SetCharset("utf-8"). |
|
|
|
|
|
SetSignType(rsa_type). |
|
|
|
|
|
SetPrivateKeyType(pkcs_type) |
|
|
|
|
|
//新支付宝支付 |
|
|
|
|
|
if paySet.PayAliUseType == "1" { |
|
|
|
|
|
client.SetAppCertSN(paySet.PayAppCertSn) |
|
|
|
|
|
aliPayRootCertSN := "687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6" |
|
|
|
|
|
client.SetAliPayRootCertSN(aliPayRootCertSN) |
|
|
|
|
|
client.SetAliPayPublicCertSN(paySet.PayAlipayrsaPublicKey) |
|
|
|
|
|
} |
|
|
|
|
|
fmt.Println(client) |
|
|
|
|
|
return client |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
func OpenAuthTokenApp(args map[string]string) (*alipay.SystemOauthTokenResponse, error) { |
|
|
|
|
|
client := Comm(args) |
|
|
|
|
|
if client == nil { |
|
|
|
|
|
return nil, errors.New("获取失败") |
|
|
|
|
|
} |
|
|
|
|
|
//请求参数 |
|
|
|
|
|
body := make(gopay.BodyMap) |
|
|
|
|
|
body.Set("grant_type", "authorization_code") |
|
|
|
|
|
body.Set("code", args["code"]) |
|
|
|
|
|
|
|
|
|
|
|
payParam, err := client.SystemOauthToken(body) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
return nil, err |
|
|
|
|
|
} |
|
|
|
|
|
return payParam, nil |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func CreditagreementSign(args map[string]string) (string, error) { |
|
|
|
|
|
client := Comm(args) |
|
|
|
|
|
client.AppAuthToken = args["app_auth_token"] |
|
|
|
|
|
if client == nil { |
|
|
|
|
|
return "", errors.New("获取失败") |
|
|
|
|
|
} |
|
|
|
|
|
//请求参数 |
|
|
|
|
|
bm := make(gopay.BodyMap) |
|
|
|
|
|
// biz_content |
|
|
|
|
|
bm.SetBodyMap("biz_content", func(bz gopay.BodyMap) { |
|
|
|
|
|
bz.Set("out_agreement_no", args["out_agreement_no"]) |
|
|
|
|
|
bz.Set("zm_service_id", args["zm_service_id"]) |
|
|
|
|
|
bz.Set("category_id", args["category_id"]) |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
aliPsp := "" |
|
|
|
|
|
bs, err := PostAliPayAPISelfV2(client, bm, "zhima.credit.payafteruse.creditagreement.sign", &aliPsp) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
xlog.Error(err) |
|
|
|
|
|
return "", err |
|
|
|
|
|
} |
|
|
|
|
|
return zhios_pay_utils.AnyToString(bs), nil |
|
|
|
|
|
} |
|
|
|
|
|
func CreditagreementQuery(args map[string]string) (*md.ZhimaCreditPayafteruseCreditagreementQuery, error) { |
|
|
|
|
|
client := Comm(args) |
|
|
|
|
|
client.AppAuthToken = args["app_auth_token"] |
|
|
|
|
|
if client == nil { |
|
|
|
|
|
return nil, errors.New("获取失败") |
|
|
|
|
|
} |
|
|
|
|
|
//请求参数 |
|
|
|
|
|
bm := make(gopay.BodyMap) |
|
|
|
|
|
// biz_content |
|
|
|
|
|
bm.SetBodyMap("biz_content", func(bz gopay.BodyMap) { |
|
|
|
|
|
bz.Set("out_agreement_no", args["out_agreement_no"]) |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
var aliPsp md.ZhimaCreditPayafteruseCreditagreementQuery |
|
|
|
|
|
bs, err := PostAliPayAPISelfV2(client, bm, "zhima.credit.payafteruse.creditagreement.query", &aliPsp) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
xlog.Error(err) |
|
|
|
|
|
return nil, err |
|
|
|
|
|
} |
|
|
|
|
|
res := bs.(*md.ZhimaCreditPayafteruseCreditagreementQuery) |
|
|
|
|
|
return res, nil |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//信用下单 |
|
|
|
|
|
func CreditagreementOrder(args map[string]string) (*md.ZhimaCreditPayafteruseCreditbizorderOrder, error) { |
|
|
|
|
|
client := Comm(args) |
|
|
|
|
|
client.AppAuthToken = args["app_auth_token"] |
|
|
|
|
|
if client == nil { |
|
|
|
|
|
return nil, errors.New("获取失败") |
|
|
|
|
|
} |
|
|
|
|
|
//请求参数 |
|
|
|
|
|
bm := make(gopay.BodyMap) |
|
|
|
|
|
// biz_content |
|
|
|
|
|
bm.SetBodyMap("biz_content", func(bz gopay.BodyMap) { |
|
|
|
|
|
bz.Set("out_order_no", args["out_order_no"]) |
|
|
|
|
|
bz.Set("credit_agreement_id", args["credit_agreement_id"]) |
|
|
|
|
|
bz.Set("category_id", args["category_id"]) |
|
|
|
|
|
bz.Set("order_amount", args["order_amount"]) |
|
|
|
|
|
bz.Set("amount_type", args["amount_type"]) |
|
|
|
|
|
bz.Set("subject", args["subject"]) |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
var aliPsp md.ZhimaCreditPayafteruseCreditbizorderOrder |
|
|
|
|
|
bs, err := PostAliPayAPISelfV2(client, bm, "zhima.credit.payafteruse.creditbizorder.order", &aliPsp) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
xlog.Error(err) |
|
|
|
|
|
return nil, err |
|
|
|
|
|
} |
|
|
|
|
|
res := bs.(*md.ZhimaCreditPayafteruseCreditbizorderOrder) |
|
|
|
|
|
return res, nil |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//查询信用订单 |
|
|
|
|
|
func CreditagreementOrderQuery(args map[string]string) (*md.ZhimaCreditPayafteruseCreditbizorderQuery, error) { |
|
|
|
|
|
client := Comm(args) |
|
|
|
|
|
client.AppAuthToken = args["app_auth_token"] |
|
|
|
|
|
if client == nil { |
|
|
|
|
|
return nil, errors.New("获取失败") |
|
|
|
|
|
} |
|
|
|
|
|
//请求参数 |
|
|
|
|
|
bm := make(gopay.BodyMap) |
|
|
|
|
|
// biz_content |
|
|
|
|
|
bm.SetBodyMap("biz_content", func(bz gopay.BodyMap) { |
|
|
|
|
|
bz.Set("credit_biz_order_id", args["credit_biz_order_id"]) |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
var aliPsp md.ZhimaCreditPayafteruseCreditbizorderQuery |
|
|
|
|
|
bs, err := PostAliPayAPISelfV2(client, bm, "zhima.credit.payafteruse.creditbizorder.query", &aliPsp) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
xlog.Error(err) |
|
|
|
|
|
return nil, err |
|
|
|
|
|
} |
|
|
|
|
|
res := bs.(*md.ZhimaCreditPayafteruseCreditbizorderQuery) |
|
|
|
|
|
return res, nil |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//转账 |
|
|
|
|
|
func AlipayFundTransUniTransfer(args map[string]string) (*md.AlipayFundTransUniTransfer, error) { |
|
|
|
|
|
client := Comm(args) |
|
|
|
|
|
if client == nil { |
|
|
|
|
|
return nil, errors.New("获取失败") |
|
|
|
|
|
} |
|
|
|
|
|
//请求参数 |
|
|
|
|
|
bm := make(gopay.BodyMap) |
|
|
|
|
|
// biz_content |
|
|
|
|
|
payeeInfo := map[string]string{ |
|
|
|
|
|
"identity": args["identity"], |
|
|
|
|
|
"identity_type": args["identity_type"], |
|
|
|
|
|
"name": args["name"], |
|
|
|
|
|
} |
|
|
|
|
|
bm.SetBodyMap("biz_content", func(bz gopay.BodyMap) { |
|
|
|
|
|
bz.Set("out_biz_no", args["out_biz_no"]) |
|
|
|
|
|
bz.Set("trans_amount", args["trans_amount"]) |
|
|
|
|
|
bz.Set("product_code", args["product_code"]) |
|
|
|
|
|
bz.Set("biz_scene", args["biz_scene"]) |
|
|
|
|
|
bz.Set("order_title", args["order_title"]) |
|
|
|
|
|
bz.Set("payee_info", payeeInfo) |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
var aliPsp md.AlipayFundTransUniTransfer |
|
|
|
|
|
bs, err := PostAliPayAPISelfV2(client, bm, "alipay.fund.trans.uni.transfer", &aliPsp) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
xlog.Error(err) |
|
|
|
|
|
return nil, err |
|
|
|
|
|
} |
|
|
|
|
|
res := bs.(*md.AlipayFundTransUniTransfer) |
|
|
|
|
|
return res, nil |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//扣款 |
|
|
|
|
|
func AlipayTradePay(args map[string]string) { |
|
|
|
|
|
client := Comm(args) |
|
|
|
|
|
if client == nil { |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
//请求参数 |
|
|
|
|
|
bm := make(gopay.BodyMap) |
|
|
|
|
|
// biz_content |
|
|
|
|
|
extendParams := map[string]string{ |
|
|
|
|
|
"creditTradeScene": args["creditTradeScene"], |
|
|
|
|
|
} |
|
|
|
|
|
bm.SetBodyMap("biz_content", func(bz gopay.BodyMap) { |
|
|
|
|
|
bz.Set("is_async_pay", args["is_async_pay"]) |
|
|
|
|
|
bz.Set("total_amount", args["total_amount"]) |
|
|
|
|
|
bz.Set("auth_code", args["auth_code"]) |
|
|
|
|
|
bz.Set("scene", args["scene"]) |
|
|
|
|
|
bz.Set("order_title", args["order_title"]) |
|
|
|
|
|
bz.Set("extend_params", extendParams) |
|
|
|
|
|
}) |
|
|
|
|
|
var aliPsp = "" |
|
|
|
|
|
PostAliPayAPISelfV2(client, bm, "alipay.trade.pay", &aliPsp) |
|
|
|
|
|
|
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
func AlipayTradeQuery(args map[string]string) (*md.AlipayFundTransUniTransfer, error) { |
|
|
|
|
|
client := Comm(args) |
|
|
|
|
|
if client == nil { |
|
|
|
|
|
return nil, errors.New("获取失败") |
|
|
|
|
|
} |
|
|
|
|
|
//请求参数 |
|
|
|
|
|
bm := make(gopay.BodyMap) |
|
|
|
|
|
bm.SetBodyMap("biz_content", func(bz gopay.BodyMap) { |
|
|
|
|
|
bz.Set("out_trade_no", args["out_trade_no"]) |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
var aliPsp md.AlipayFundTransUniTransfer |
|
|
|
|
|
bs, err := PostAliPayAPISelfV2(client, bm, "alipay.trade.query", &aliPsp) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
xlog.Error(err) |
|
|
|
|
|
return nil, err |
|
|
|
|
|
} |
|
|
|
|
|
res := bs.(*md.AlipayFundTransUniTransfer) |
|
|
|
|
|
return res, nil |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//结束信用服务订单 |
|
|
|
|
|
func CreditagreementOrderFinish(args map[string]string) (*md.ZhimaCreditPayafteruseCreditbizorderFinish, error) { |
|
|
|
|
|
client := Comm(args) |
|
|
|
|
|
client.AppAuthToken = args["app_auth_token"] |
|
|
|
|
|
if client == nil { |
|
|
|
|
|
return nil, errors.New("获取失败") |
|
|
|
|
|
} |
|
|
|
|
|
//请求参数 |
|
|
|
|
|
bm := make(gopay.BodyMap) |
|
|
|
|
|
// biz_content |
|
|
|
|
|
bm.SetBodyMap("biz_content", func(bz gopay.BodyMap) { |
|
|
|
|
|
bz.Set("out_request_no", args["out_request_no"]) |
|
|
|
|
|
bz.Set("credit_biz_order_id", args["credit_biz_order_id"]) |
|
|
|
|
|
bz.Set("is_fulfilled", args["is_fulfilled"]) |
|
|
|
|
|
bz.Set("remark", args["remark"]) |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
var aliPsp md.ZhimaCreditPayafteruseCreditbizorderFinish |
|
|
|
|
|
bs, err := PostAliPayAPISelfV2(client, bm, "zhima.credit.payafteruse.creditbizorder.finish", &aliPsp) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
xlog.Error(err) |
|
|
|
|
|
return nil, err |
|
|
|
|
|
} |
|
|
|
|
|
res := bs.(*md.ZhimaCreditPayafteruseCreditbizorderFinish) |
|
|
|
|
|
return res, nil |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func PostAliPayAPISelfV2(a *alipay.Client, bm gopay.BodyMap, method string, aliRsp interface{}) (aliRsps interface{}, err error) { |
|
|
|
|
|
var ( |
|
|
|
|
|
bs, bodyBs []byte |
|
|
|
|
|
) |
|
|
|
|
|
// check if there is biz_content |
|
|
|
|
|
bz := bm.GetInterface("biz_content") |
|
|
|
|
|
if bzBody, ok := bz.(gopay.BodyMap); ok { |
|
|
|
|
|
if bodyBs, err = json.Marshal(bzBody); err != nil { |
|
|
|
|
|
return aliRsp, fmt.Errorf("json.Marshal(%v):%w", bzBody, err) |
|
|
|
|
|
} |
|
|
|
|
|
bm.Set("biz_content", string(bodyBs)) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if bs, err = doAliPaySelf(a, bm, method); err != nil { |
|
|
|
|
|
return aliRsp, err |
|
|
|
|
|
} |
|
|
|
|
|
switch method { |
|
|
|
|
|
case "alipay.trade.app.pay": |
|
|
|
|
|
return string(bs), nil |
|
|
|
|
|
case "alipay.trade.wap.pay", "alipay.trade.page.pay", "alipay.user.certify.open.certify", "zhima.credit.payafteruse.creditagreement.sign": |
|
|
|
|
|
return string(bs), nil |
|
|
|
|
|
default: |
|
|
|
|
|
if err = json.Unmarshal(bs, aliRsp); err != nil { |
|
|
|
|
|
return aliRsp, err |
|
|
|
|
|
} |
|
|
|
|
|
return aliRsp, nil |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 向支付宝发送自定义请求 |
|
|
|
|
|
func doAliPaySelf(a *alipay.Client, bm gopay.BodyMap, method string) (bs []byte, err error) { |
|
|
|
|
|
var ( |
|
|
|
|
|
url, sign string |
|
|
|
|
|
) |
|
|
|
|
|
bm.Set("method", method) |
|
|
|
|
|
|
|
|
|
|
|
// check public parameter |
|
|
|
|
|
checkPublicParam(a, bm) |
|
|
|
|
|
|
|
|
|
|
|
// check sign |
|
|
|
|
|
if bm.GetString("sign") == "" { |
|
|
|
|
|
sign, err = alipay.GetRsaSign(bm, bm.GetString("sign_type"), a.PrivateKeyType, a.PrivateKey) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
return nil, fmt.Errorf("GetRsaSign Error: %v", err) |
|
|
|
|
|
} |
|
|
|
|
|
bm.Set("sign", sign) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if a.DebugSwitch == gopay.DebugOn { |
|
|
|
|
|
req, _ := json.Marshal(bm) |
|
|
|
|
|
xlog.Debugf("Alipay_Request: %s", req) |
|
|
|
|
|
} |
|
|
|
|
|
param := alipay.FormatURLParam(bm) |
|
|
|
|
|
switch method { |
|
|
|
|
|
case "alipay.trade.app.pay": |
|
|
|
|
|
return []byte(param), nil |
|
|
|
|
|
case "alipay.trade.wap.pay", "alipay.trade.page.pay", "alipay.user.certify.open.certify", "zhima.credit.payafteruse.creditagreement.sign": |
|
|
|
|
|
return []byte(baseUrl + "?" + param), nil |
|
|
|
|
|
default: |
|
|
|
|
|
httpClient := xhttp.NewClient() |
|
|
|
|
|
url = baseUrlUtf8 |
|
|
|
|
|
res, bs, errs := httpClient.Type(xhttp.TypeForm).Post(url).SendString(param).EndBytes() |
|
|
|
|
|
if len(errs) > 0 { |
|
|
|
|
|
return nil, errs[0] |
|
|
|
|
|
} |
|
|
|
|
|
if a.DebugSwitch == gopay.DebugOn { |
|
|
|
|
|
xlog.Debugf("Alipay_Response: %s%d %s%s", xlog.Red, res.StatusCode, xlog.Reset, string(bs)) |
|
|
|
|
|
} |
|
|
|
|
|
if res.StatusCode != 200 { |
|
|
|
|
|
return nil, fmt.Errorf("HTTP Request Error, StatusCode = %d", res.StatusCode) |
|
|
|
|
|
} |
|
|
|
|
|
return bs, nil |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
func checkPublicParam(a *alipay.Client, bm gopay.BodyMap) { |
|
|
|
|
|
bm.Set("format", "JSON") |
|
|
|
|
|
|
|
|
|
|
|
if bm.GetString("app_id") == "" && a.AppId != util.NULL { |
|
|
|
|
|
bm.Set("app_id", a.AppId) |
|
|
|
|
|
} |
|
|
|
|
|
if bm.GetString("app_cert_sn") == "" && a.AppCertSN != util.NULL { |
|
|
|
|
|
bm.Set("app_cert_sn", a.AppCertSN) |
|
|
|
|
|
} |
|
|
|
|
|
if bm.GetString("alipay_root_cert_sn") == "" && a.AliPayRootCertSN != util.NULL { |
|
|
|
|
|
bm.Set("alipay_root_cert_sn", a.AliPayRootCertSN) |
|
|
|
|
|
} |
|
|
|
|
|
if bm.GetString("return_url") == "" && a.ReturnUrl != util.NULL { |
|
|
|
|
|
bm.Set("return_url", a.ReturnUrl) |
|
|
|
|
|
} |
|
|
|
|
|
bm.Set("charset", "utf-8") |
|
|
|
|
|
if bm.GetString("charset") == "" && a.Charset != util.NULL { |
|
|
|
|
|
bm.Set("charset", a.Charset) |
|
|
|
|
|
} |
|
|
|
|
|
bm.Set("sign_type", alipay.RSA2) |
|
|
|
|
|
if bm.GetString("sign_type") == "" && a.SignType != util.NULL { |
|
|
|
|
|
bm.Set("sign_type", a.SignType) |
|
|
|
|
|
} |
|
|
|
|
|
bm.Set("timestamp", time.Now().Format(util.TimeLayout)) |
|
|
|
|
|
|
|
|
|
|
|
bm.Set("version", "1.0") |
|
|
|
|
|
if bm.GetString("notify_url") == "" && a.NotifyUrl != util.NULL { |
|
|
|
|
|
bm.Set("notify_url", a.NotifyUrl) |
|
|
|
|
|
} |
|
|
|
|
|
if bm.GetString("app_auth_token") == "" && a.AppAuthToken != util.NULL { |
|
|
|
|
|
bm.Set("app_auth_token", a.AppAuthToken) |
|
|
|
|
|
} |
|
|
|
|
|
if bm.GetString("auth_token") == "" && a.AuthToken != util.NULL { |
|
|
|
|
|
bm.Set("auth_token", a.AuthToken) |
|
|
|
|
|
} |
|
|
|
|
|
} |