diff --git a/api/proto/vas/proto/pay.go b/api/proto/vas/proto/pay.go index 0684d5e5..2ff3133a 100644 --- a/api/proto/vas/proto/pay.go +++ b/api/proto/vas/proto/pay.go @@ -35,13 +35,15 @@ const ( PayTypeYeepayAlipayH5 = "yeepay_alipay_h5" // 易宝 微信h5支付 PayTypeYeepayWxpayH5 = "yeepay_wxpay_h5" // 易宝 微信h5支付 PayTypeCoin = "coin" // 金币支付 + PayTypeApplepay = "applepay" // ios iap 支付 ) const ( - CallBackPayTypeAlipay = "alipay" - CallBackPayTypeWxpay = "wxpay" - CallBackPayTypeYeepay = "yeepay" - CallBackPayTypeCoin = "coin" + CallBackPayTypeAlipay = "alipay" + CallBackPayTypeWxpay = "wxpay" + CallBackPayTypeYeepay = "yeepay" + CallBackPayTypeCoin = "coin" + CallBackPayTypeApplepay = "applepay" ) const ( diff --git a/api/proto/zone/proto/zone_vo_api.go b/api/proto/zone/proto/zone_vo_api.go index 2e93e09c..8d87fdd5 100644 --- a/api/proto/zone/proto/zone_vo_api.go +++ b/api/proto/zone/proto/zone_vo_api.go @@ -9,9 +9,12 @@ import ( type ApiZoneVO struct { *dbstruct.Zone AdmissionPrice int64 `json:"admission_price" bson:"admission_price"` // 空间解锁价格, 单位: 分 + AdmissionCoinPrice int64 `json:"admission_coin_price" bson:"admission_coin_price"` // 空间解锁价格, 单位: 金币 IronfanshipPrice int64 `json:"ironfanship_price" bson:"ironfanship_price"` // 铁粉解锁价格, 单位: 分 + IronfanshipCoinPrice int64 `json:"ironfanship_coin_price" bson:"ironfanship_coin_price"` // 铁粉解锁价格, 单位: 金币 IsSuperfanshipEnabled int `json:"is_superfanship_enabled" bson:"is_superfanship_enabled"` // 是否开启超粉空间 0: 关闭, 1: 开启 SuperfanshipPrice int64 `json:"superfanship_price" bson:"superfanship_price"` // 超粉价格, 单位: 分 + SuperfanshipCoinPrice int64 `json:"superfanship_coin_price" bson:"superfanship_coin_price"` // 超粉价格, 单位: 金币 SuperfanshipValidPeriod int `json:"superfanship_valid_period" bson:"superfanship_valid_period"` // 超粉有效期类型, SuperfanshipValidPeriod* IsSuperfanshipGiveWechat int `json:"is_superfanship_give_wechat" bson:"is_superfanship_give_wechat"` // 是否开启超粉空间赠送微信 0: 不赠送, 1: 赠送 StreamerExt *streamerproto.ApiListExtVO `json:"streamer_ext"` @@ -55,9 +58,12 @@ func (vo *ApiZoneVO) SetIsSuperfanshipUnlocked(is_superfanship_unlocked int64) { func (vo *ApiZoneVO) CopyZoneVas(zv *dbstruct.ZoneVas) { if zv != nil { vo.AdmissionPrice = zv.AdmissionPrice + vo.AdmissionCoinPrice = zv.GetAdmissionCoinPrice() vo.IronfanshipPrice = zv.IronfanshipPrice + vo.IronfanshipCoinPrice = zv.GetIronfanshipCoinPrice() vo.IsSuperfanshipEnabled = zv.IsSuperfanshipEnabled vo.SuperfanshipPrice = zv.SuperfanshipPrice + vo.SuperfanshipCoinPrice = zv.GetSuperfanshipCoinPrice() vo.SuperfanshipValidPeriod = zv.SuperfanshipValidPeriod vo.IsSuperfanshipGiveWechat = zv.IsSuperfanshipGiveWechat } diff --git a/api/proto/zonemoment/proto/zonemoment_vo_api.go b/api/proto/zonemoment/proto/zonemoment_vo_api.go index 9f5afae6..4d766772 100644 --- a/api/proto/zonemoment/proto/zonemoment_vo_api.go +++ b/api/proto/zonemoment/proto/zonemoment_vo_api.go @@ -15,6 +15,7 @@ type ApiZoneMomentVO struct { IsZoneMomentUnlocked int64 `json:"is_zone_moment_unlocked"` Expenditure int64 `json:"expenditure"` IronfanshipPrice int64 `json:"ironfanship_price"` + IronfanshipCoinPrice int64 `json:"ironfanship_coin_price"` BuyerCnt int64 `json:"buyer_cnt"` // 动态购买人数 IsSuperfanshipEnabled int64 `json:"is_superfanship_enabled"` } diff --git a/app/mix/controller/applepay_callback.go b/app/mix/controller/applepay_callback.go index acbab2db..818daf40 100644 --- a/app/mix/controller/applepay_callback.go +++ b/app/mix/controller/applepay_callback.go @@ -5,19 +5,29 @@ import ( "github.com/gin-gonic/gin" "io/ioutil" "net/http" - "service/bizcommon/util" + "service/app/mix/service" "service/library/logger" + "service/library/payclients/applepaycli" ) func ApplepayCallback(ctx *gin.Context) { - var bodyParam map[string]interface{} + var notify = applepaycli.ApplepayRevenueNotify{} buf, err := ioutil.ReadAll(ctx.Request.Body) logger.Info("ApplepayCallback body: %v", string(buf)) - err = json.Unmarshal(buf, &bodyParam) + + err = json.Unmarshal(buf, ¬ify) if err != nil { logger.Error("arg parse fail: %v", err) ReplyJsonError(ctx, http.StatusBadRequest, "参数解析失败") return } - logger.Info("ApplepayCallback param: %v", util.ToJson(bodyParam)) + + err = service.DefaultService.ApplepayCallback(ctx, notify) + if err != nil { + logger.Error("ApplepayCallback fail: %v", err) + ReplyJsonError(ctx, -1, err.Error()) + return + } + + ctx.String(200, "success") } diff --git a/app/mix/controller/vas.go b/app/mix/controller/vas.go index 840ada60..7261b82e 100644 --- a/app/mix/controller/vas.go +++ b/app/mix/controller/vas.go @@ -6,6 +6,7 @@ import ( "service/app/mix/service" "service/bizcommon/common" "service/bizcommon/util" + "service/dbstruct" "service/library/logger" "time" @@ -50,6 +51,12 @@ func CreateOrder(ctx *gin.Context) { ReplyErrCodeMsg(ctx, errcode.ErrCodeBadParam) return } + // 检查能用金币支付的商品 + if req.PayType == vasproto.PayTypeCoin && !dbstruct.CoinPayValidProductIdMap[req.ProductId] { + ReplyErrorMsg(ctx, "商品错误") + return + } + data, ec, err := service.DefaultService.CreateOrder(ctx, req) if ec != errcode.ErrCodeVasSrvOk { logger.Error("CreateOrder fail, req: %v, ec: %v", util.ToJson(req), ec) diff --git a/app/mix/controller/zone_vas_api.go b/app/mix/controller/zone_vas_api.go index b0dec7e4..c0eb0809 100644 --- a/app/mix/controller/zone_vas_api.go +++ b/app/mix/controller/zone_vas_api.go @@ -6,6 +6,7 @@ import ( vasproto "service/api/proto/vas/proto" "service/app/mix/service" "service/bizcommon/util" + "service/dbstruct" "service/library/logger" ) @@ -16,6 +17,12 @@ func ZoneCreateOrder(ctx *gin.Context) { ReplyErrCodeMsg(ctx, errcode.ErrCodeBadParam) return } + // 检查能用金币支付的商品 + if req.PayType == vasproto.PayTypeCoin && !dbstruct.CoinPayValidProductIdMap[req.ProductId] { + ReplyErrorMsg(ctx, "商品错误") + return + } + data, ec, err := service.DefaultService.ZoneCreateOrder(ctx, req) if ec != errcode.ErrCodeVasSrvOk { logger.Error("ZoneCreateOrder fail, req: %v, ec: %v", util.ToJson(req), ec) diff --git a/app/mix/service/logic/vas.go b/app/mix/service/logic/vas.go index 37b4d3ab..9e2e4902 100644 --- a/app/mix/service/logic/vas.go +++ b/app/mix/service/logic/vas.go @@ -428,7 +428,7 @@ func (v *Vas) CreateOrderByCoin(ctx *gin.Context, req *vasproto.CreateOrderReq, logger.Error("CheckWalletExist fail, mid: %v, err: %v", req.Mid, err) return } - if wallet.GetCoins() <= priceCoin { + if wallet.GetCoins() < priceCoin { err = errs.ErrVasNoEnoughCoin logger.Error("not enough coin, mid: %v, coins: %v, err: %v", req.Mid, wallet.Coins, err) return @@ -454,7 +454,7 @@ func (v *Vas) CreateOrderByCoin(ctx *gin.Context, req *vasproto.CreateOrderReq, Oid3: goproto.String(req.Oid3), ProductId: goproto.String(req.ProductId), PayType: goproto.String(req.PayType), - PayAmount: goproto.Int64(product.RealPrice), + PayAmount: goproto.Int64(priceCoin), Coins: goproto.Int64(product.ValueCoins), OrderStatus: goproto.Int32(dbstruct.VasOrderStatusInit), OrderFrom: goproto.String(req.From), @@ -1547,6 +1547,9 @@ func (v *Vas) PayCallback(ctx *gin.Context, p *vasproto.PayCallbackParamIn) erro Count: goproto.Int64(order.GetCoins()), Ct: goproto.Int64(time.Now().Unix()), } + if order.GetPayType() == vasproto.PayTypeCoin { + ch.TypeId = goproto.String(ch.GetTypeId() + ":_:pay_type=coin") + } switch product.Id { case dbstruct.ProductIdMembership: ch.SType = goproto.Int32(dbstruct.CHSTypeChargeMembership) @@ -2266,6 +2269,9 @@ func (v *Vas) UnlockMembership(ctx *gin.Context, tx *sqlx.Tx, mid int64, product // 计算收入 var totalDias = int64(float64(order.GetPayAmount()) / 100.0 * 10.0) + if order.GetPayType() == vasproto.PayTypeCoin { + totalDias = order.GetPayAmount() + } incomeList, err := v.calcAndUpdateIncome(ctx, tx, order.GetUid(), mid, order.GetDid(), orderId, order.GetProductId(), totalDias, dbstruct.CHSTypeIncomeMembership) if err != nil { logger.Error("calcAndUpdateIncome fail, order: %v, err: %v", util.ToJson(order), err) @@ -2598,7 +2604,7 @@ func (v *Vas) refundMembership(ctx *gin.Context, order *dbstruct.Order, req *vas } // 退款 - err = v.payRefund(ctx, order) + err = v.payRefund(ctx, tx, order) if err != nil { return err } @@ -2696,7 +2702,7 @@ func (v *Vas) refundCoins(ctx *gin.Context, order *dbstruct.Order, req *vasproto } // 退款 - err = v.payRefund(ctx, order) + err = v.payRefund(ctx, tx, order) if err != nil { return err } @@ -2794,7 +2800,7 @@ func (v *Vas) refundMoneyContactWechat(ctx *gin.Context, order *dbstruct.Order, } // 退款 - err = v.payRefund(ctx, order) + err = v.payRefund(ctx, tx, order) if err != nil { return err } @@ -3020,7 +3026,7 @@ func (v *Vas) refundZoneAdmission(ctx *gin.Context, order *dbstruct.Order, req * } // 退款 - err = v.payRefund(ctx, order) + err = v.payRefund(ctx, tx, order) if err != nil { return err } @@ -3247,7 +3253,7 @@ func (v *Vas) refundZoneMoment(ctx *gin.Context, order *dbstruct.Order, req *vas } // 退款 - err = v.payRefund(ctx, order) + err = v.payRefund(ctx, tx, order) if err != nil { return err } @@ -3483,7 +3489,7 @@ func (v *Vas) refundZoneSuperfanship(ctx *gin.Context, order *dbstruct.Order, re } // 退款 - err = v.payRefund(ctx, order) + err = v.payRefund(ctx, tx, order) if err != nil { return err } @@ -3706,7 +3712,7 @@ func (v *Vas) refundCoinContactWechat(ctx *gin.Context, order *dbstruct.CoinOrde return nil } -func (v *Vas) payRefund(ctx *gin.Context, order *dbstruct.Order) error { +func (v *Vas) payRefund(ctx *gin.Context, tx *sqlx.Tx, order *dbstruct.Order) error { var ( orderId = order.GetID() ) @@ -3784,6 +3790,12 @@ func (v *Vas) payRefund(ctx *gin.Context, order *dbstruct.Order) error { return err } logger.Info("wxpayCli.RefundOne success, orderId: %v", orderId) + case vasproto.PayTypeCoin: + err := v.store.IncCoins(ctx, tx, order.GetMid(), order.GetPayAmount()) + if err != nil { + logger.Error("coin.RefundOne fail, orderId: %v, err: %v", orderId, err) + return err + } } return nil } diff --git a/app/mix/service/logic/vas_zone.go b/app/mix/service/logic/vas_zone.go index e3cb67e5..ac7f9614 100644 --- a/app/mix/service/logic/vas_zone.go +++ b/app/mix/service/logic/vas_zone.go @@ -294,7 +294,10 @@ func (v *Vas) UnlockZoneMoment(ctx *gin.Context, tx *sqlx.Tx, order *dbstruct.Or } // 计算收入 - totalDias := int64(float64(order.GetPayAmount()) / 100.0 * 10.0) + var totalDias = int64(float64(order.GetPayAmount()) / 100.0 * 10.0) + if order.GetPayType() == vasproto.PayTypeCoin { + totalDias = order.GetPayAmount() + } incomeList, err := v.calcAndUpdateIncome(ctx, tx, order.GetUid(), mid, order.GetDid(), orderId, order.GetProductId(), totalDias, dbstruct.CHSTypeIncomeZoneStreamer) if err != nil { logger.Error("calcAndUpdateIncome fail, order: %v, err: %v", util.ToJson(order), err) @@ -374,7 +377,10 @@ func (v *Vas) UnlockZoneAdmission(ctx *gin.Context, tx *sqlx.Tx, order *dbstruct } // 计算收入 - totalDias := int64(float64(order.GetPayAmount()) / 100.0 * 10.0) + var totalDias = int64(float64(order.GetPayAmount()) / 100.0 * 10.0) + if order.GetPayType() == vasproto.PayTypeCoin { + totalDias = order.GetPayAmount() + } incomeList, err := v.calcAndUpdateIncome(ctx, tx, order.GetUid(), mid, order.GetDid(), orderId, order.GetProductId(), totalDias, dbstruct.CHSTypeIncomeZoneStreamer) if err != nil { logger.Error("calcAndUpdateIncome fail, order: %v, err: %v", util.ToJson(order), err) @@ -424,7 +430,10 @@ func (v *Vas) UnlockZoneSuperfanship(ctx *gin.Context, tx *sqlx.Tx, order *dbstr } // 计算收入 - totalDias := int64(float64(order.GetPayAmount()) / 100.0 * 10.0) + var totalDias = int64(float64(order.GetPayAmount()) / 100.0 * 10.0) + if order.GetPayType() == vasproto.PayTypeCoin { + totalDias = order.GetPayAmount() + } incomeList, err := v.calcAndUpdateIncome(ctx, tx, order.GetUid(), mid, order.GetDid(), orderId, order.GetProductId(), totalDias, dbstruct.CHSTypeIncomeZoneStreamer) if err != nil { logger.Error("calcAndUpdateIncome fail, order: %v, err: %v", util.ToJson(order), err) diff --git a/app/mix/service/service.go b/app/mix/service/service.go index b7837327..0d51df0a 100644 --- a/app/mix/service/service.go +++ b/app/mix/service/service.go @@ -1040,6 +1040,7 @@ func (s *Service) GetMembershipProductList(ctx *gin.Context, req *vasproto.GetMe ec = errcode.ErrCodeVasSrvFail return } + product.RealCoinPrice = product.GetRealPriceCoin() data = &vasproto.GetMembershipProductListData{ Product: product, } diff --git a/app/mix/service/utilservice.go b/app/mix/service/utilservice.go index ed4a7918..00f43e78 100644 --- a/app/mix/service/utilservice.go +++ b/app/mix/service/utilservice.go @@ -1843,6 +1843,7 @@ func (s *Service) utilFillZoneMomentsWithApiVOInfo(ctx *gin.Context, list []*dbs // 7.填充所有信息 for _, vo := range volist { + vo.ZoneMoment.CoinPrice = vo.ZoneMoment.GetCoinPrice() zid := vo.ZoneMoment.GetZid() @@ -1886,6 +1887,7 @@ func (s *Service) utilFillZoneMomentsWithApiVOInfo(ctx *gin.Context, list []*dbs // 铁粉价格 if zv, ok := zvMap[zid]; ok { vo.IronfanshipPrice = zv.IronfanshipPrice + vo.IronfanshipCoinPrice = zv.GetIronfanshipCoinPrice() vo.IsSuperfanshipEnabled = int64(zv.IsSuperfanshipEnabled) } } diff --git a/app/mix/service/vasservice.go b/app/mix/service/vasservice.go index 0fe275c9..6898627a 100644 --- a/app/mix/service/vasservice.go +++ b/app/mix/service/vasservice.go @@ -4,16 +4,21 @@ import ( "encoding/base64" "fmt" goproto "google.golang.org/protobuf/proto" + "service/api/base" "service/api/errcode" "service/api/errs" accountproto "service/api/proto/account/proto" vasproto "service/api/proto/vas/proto" vericodeproto "service/api/proto/vericode/proto" zoneproto "service/api/proto/zone/proto" + "service/bizcommon/common" "service/bizcommon/util" "service/dbstruct" "service/library/logger" "service/library/mycrypto" + "service/library/payclients/applepaycli" + "strconv" + "strings" "github.com/gin-gonic/gin" ) @@ -313,6 +318,14 @@ func (s *Service) chListCharge(ctx *gin.Context, chList []*dbstruct.ConsumeHisto item.Desc = "解锁空间超粉退款" item.Change = fmt.Sprintf("+%.1f元", float64(util.AbsInt64(chDB.GetChange()))/100.0) } + if strings.Contains(chDB.GetTypeId(), ":_:pay_type=coin") && strings.Contains(item.Change, "元") { + oldChange := item.Change + newChange := "" + if len(item.Change) > 0 { + newChange += oldChange[:1] + } + newChange += fmt.Sprintf("%d金币", chDB.GetChange()) + } list = append(list, item) } @@ -1079,3 +1092,52 @@ func (s *Service) OpManualUnlockWechat(ctx *gin.Context, req *zoneproto.OpManual } return } + +func (s *Service) ApplepayCallback(ctx *gin.Context, notify applepaycli.ApplepayRevenueNotify) error { + var ( + event = notify.Event + mid, _ = strconv.ParseInt(event.AppUserID, 10, 64) + ) + + switch event.Type { + case applepaycli.NotifyTypeNonRenewingPurchase: + // 验证用户 + acnt, _ := _DefaultAccount.GetAccountMapByMids(ctx, []int64{mid}) + if _, ok := acnt[mid]; !ok { + err := fmt.Errorf("invalid user") + logger.Error("ApplepayCallback fail, invalid user, mid: %v, notify: %v", mid, notify) + return err + } + + // 先创建订单 + createOrderParam := &vasproto.CreateOrderReq{ + BaseRequest: base.BaseRequest{ + Mid: mid, + DevType: common.DeviceTypeIos, + }, + ProductId: event.ProductID, + PayType: vasproto.PayTypeCoin, + Oid3: event.AppID, + } + data, err := _DefaultVas.CreateOrder(ctx, createOrderParam) + if err != nil { + logger.Error("CreateOrder fail, param: %v, err: %v", createOrderParam, err) + return err + } + orderId := data.OrderId + + // 手动回调 + payCallbackParam := &vasproto.PayCallbackParamIn{ + OrderId: orderId, + OutOrderId: event.TransactionID, + CallbackPayType: vasproto.CallBackPayTypeApplepay, + } + err = _DefaultVas.PayCallback(ctx, payCallbackParam) + if err != nil { + logger.Error("PayCallback fail, param: %v, err: %v", payCallbackParam, err) + return err + } + } + + return nil +} diff --git a/bizcommon/util/util.go b/bizcommon/util/util.go index 4604f20e..f4ed5eed 100644 --- a/bizcommon/util/util.go +++ b/bizcommon/util/util.go @@ -201,3 +201,7 @@ func VerisonCompare(ver1 string, ver2 string) (bool, error) { } return false, nil } + +func RoundUp(num float64) int64 { + return int64(math.Ceil(num)) +} diff --git a/bizcommon/util/x_test.go b/bizcommon/util/x_test.go new file mode 100644 index 00000000..c2d66fe5 --- /dev/null +++ b/bizcommon/util/x_test.go @@ -0,0 +1,7 @@ +package util + +import "testing" + +func TestRound(t *testing.T) { + Round() +} diff --git a/dbstruct/product.go b/dbstruct/product.go index 936eb208..45c4218b 100644 --- a/dbstruct/product.go +++ b/dbstruct/product.go @@ -1,5 +1,7 @@ package dbstruct +import "service/bizcommon/util" + // 商品id const ( ProductIdOpCoin = "op_coin" // op充值的金币 @@ -50,6 +52,13 @@ var ProductIdDescMap = map[string]string{ ProductIdH5ZoneSuperfanship: "解锁超粉", } +var CoinPayValidProductIdMap = map[string]bool{ + ProductIdMembership: true, + ProductIdH5ZoneMoment: true, + ProductIdH5ZoneAdmission: true, + ProductIdH5ZoneSuperfanship: true, +} + // 商品类型 const ( ProductTypeCoins = "coins" // 商品类型:金币 @@ -98,5 +107,5 @@ type Product struct { } func (p Product) GetRealPriceCoin() int64 { - return p.RealPrice / 10 + return util.RoundUp(float64(p.RealPrice) / 10.0) } diff --git a/dbstruct/vas_mongo.go b/dbstruct/vas_mongo.go index b7a86e3d..a8cf801e 100644 --- a/dbstruct/vas_mongo.go +++ b/dbstruct/vas_mongo.go @@ -1,6 +1,9 @@ package dbstruct -import "math" +import ( + "math" + "service/bizcommon/util" +) // 用户增值信息 const ( @@ -122,6 +125,18 @@ func (p ZoneVas) GetSuperfanshipDurationDesc() string { } } +func (p ZoneVas) GetAdmissionCoinPrice() int64 { + return util.RoundUp(float64(p.AdmissionPrice) / 10.0) +} + +func (p ZoneVas) GetIronfanshipCoinPrice() int64 { + return util.RoundUp(float64(p.IronfanshipPrice) / 10.0) +} + +func (p ZoneVas) GetSuperfanshipCoinPrice() int64 { + return util.RoundUp(float64(p.SuperfanshipPrice) / 10.0) +} + // 空间动态价格 type ZoneMomentPrice struct { MomentId int64 `json:"id" bson:"_id"` // 动态id diff --git a/dbstruct/zonemoment.go b/dbstruct/zonemoment.go index ce9d7da6..f9cf425c 100644 --- a/dbstruct/zonemoment.go +++ b/dbstruct/zonemoment.go @@ -1,5 +1,7 @@ package dbstruct +import "service/bizcommon/util" + type ZoneMoment struct { Id *int64 `json:"id" bson:"_id"` // 私密圈动态表id Mid *int64 `json:"mid" bson:"mid"` // 用户表id @@ -31,7 +33,7 @@ type ZoneMoment struct { Ct *int64 `json:"ct" bson:"ct"` // 创建时间 Ut *int64 `json:"ut" bson:"ut"` // 更新时间 DelFlag *int64 `json:"del_flag" bson:"del_flag"` // 删除标记 - + CoinPrice int64 `json:"coin_price"` // 单帖价格,单位人民币分 } func (p *ZoneMoment) GetId() int64 { @@ -136,3 +138,7 @@ func (p *ZoneMomentStatInfo) GetCount() int64 { } return 0 } + +func (p *ZoneMoment) GetCoinPrice() int64 { + return util.RoundUp(float64(p.CoinPrice) / 10.0) +} diff --git a/library/payclients/applepaycli/proto.go b/library/payclients/applepaycli/proto.go new file mode 100644 index 00000000..000d5398 --- /dev/null +++ b/library/payclients/applepaycli/proto.go @@ -0,0 +1,46 @@ +package applepaycli + +const ( + NotifyTypeNonRenewingPurchase = "NON_RENEWING_PURCHASE" // 非续费型消费 +) + +type Event struct { + EventTimestampMs int64 `json:"event_timestamp_ms"` + ProductID string `json:"product_id"` + PeriodType string `json:"period_type"` + PurchasedAtMs int64 `json:"purchased_at_ms"` + ExpirationAtMs int64 `json:"expiration_at_ms"` + Environment string `json:"environment"` + EntitlementID string `json:"entitlement_id"` + EntitlementIDs []string `json:"entitlement_ids"` + PresentedOfferingID string `json:"presented_offering_id"` + TransactionID string `json:"transaction_id"` + OriginalTransactionID string `json:"original_transaction_id"` + IsFamilyShare bool `json:"is_family_share"` + CountryCode string `json:"country_code"` + AppUserID string `json:"app_user_id"` + Aliases []string `json:"aliases"` + OriginalAppUserID string `json:"original_app_user_id"` + Currency string `json:"currency"` + Price float64 `json:"price"` + PriceInPurchasedCurrency float64 `json:"price_in_purchased_currency"` + SubscriberAttributes struct { + AttConsentStatus struct { + Value string `json:"value"` + UpdatedAtMs int64 `json:"updated_at_ms"` + } `json:"$attConsentStatus"` + } `json:"subscriber_attributes"` + Store string `json:"store"` + TakehomePercentage float64 `json:"takehome_percentage"` + OfferCode string `json:"offer_code"` + TaxPercentage float64 `json:"tax_percentage"` + CommissionPercentage float64 `json:"commission_percentage"` + Type string `json:"type"` + ID string `json:"id"` + AppID string `json:"app_id"` +} + +type ApplepayRevenueNotify struct { + Event Event `json:"event"` + APIVersion string `json:"api_version"` +} diff --git a/main.go b/main.go index f0aaf4e6..196316ae 100644 --- a/main.go +++ b/main.go @@ -26,6 +26,15 @@ type C struct { } func main() { + numbers := []float64{12.9, 99.9} + + for _, number := range numbers { + rounded := util.RoundUp(number) + fmt.Printf("%.1f -> %d\n", number, rounded) + } + + return + c := C{ A: &A{Mid: 111}, B: &B{Mid: 222},