计费系统
服务端搭建
大体逻辑为客户端申请充值列表,------需修改
整体项目maven构建,所有第三方jar包配置在pom.xml中,数据库操作使用mybitas,查询后的关键数据使用memcache缓存到系统中
计费系统分为自运和渠道,自运使用支付宝等充值,后边详细介绍,先介绍渠道的充值方式
项目启动后,spring加载需要实例化的bean。编写StartbBilling类,通过PostConstruct注释启动mina编写的server服务端,
服务端使用 mina 构建,在服务由maven打包后,需要接收渠道的回调url申请,url回调申请使用spring的注释,--@RequestMapping
如
package com.example.billing.applacation.channel.lenovo;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Date;
import java.util.Map.Entry;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.json.JSONObject;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.example.billing.applacation.game.base.PushMessage;
import com.example.billing.cache.Configuration;
import com.example.billing.domain.model.ChargeChannel;
import com.example.billing.domain.model.MoneyLog;
import com.example.billing.domain.service.MoneyLogService;
import com.example.billing.util.GameUtil;
/**
* Lenovo sdk
*/
@Controller
@RequestMapping(value = "/lenovo")
//依照上边的value值,渠道回调地址为http://address:pot/项目名称/service/lenovo
public class LenovoResultAction {
//添加日志
private final static Logger logger = Logger.getLogger(LenovoResultAction.class.getName());
protected static final GsonBuilder builder = new GsonBuilder().excludeFieldsWithoutExposeAnnotation();
protected static final Gson gson = builder.create();
//注入所需业务处理bean
@Resource
MoneyLogService moneyLogService;
@Resource
private PushMessage pushMessage;
//回调方式,依据渠道的要求编写,本示例为http协议,POST方法
@RequestMapping(value = "", method = RequestMethod.GET)
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Exception {
doPost(req, resp);
}
@RequestMapping(value = "", method = RequestMethod.POST)
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws Exception {
PrintWriter writer = null;
try {
StringBuffer buffer = new StringBuffer("收到lenovo支付返回消息:");
for (Entry<String, String[]> ent : req.getParameterMap().entrySet()) {
buffer.append(ent.getKey()).append("=").append(Arrays.toString(ent.getValue())).append("|");
}
logger.info(buffer);
// 签名
String sign = req.getParameter("sign");
// 订单数据串
String transdata = req.getParameter("transdata");
// json转化为对象
JSONObject obj = new JSONObject(transdata);
// 外部订单号-商户订单号
String exorderno = obj.getString("exorderno");
// 计费支付平台的交易流水号
String transid = obj.getString("transid");
// 平台为商户应用分配的唯一编号
String appid = obj.getString("appid");
// 平台为应用内需计费商品分配的编号
String waresid = obj.getString("waresid");
// 计费类型: 0 – 开放价格 1 – 免费 2 – 按次 3 – 包自然时长 4 – 包账期 5 – 买断 6 – 包次数 7 –
// 按时长 8 – 包活跃时长 9 – 批量购买 100 – 按次免费试用 101 – 按时长免费试用
String feetype = obj.getString("feetype");
// 本次交易金额(单位:分)
String money = obj.getString("money");
// 购买数量
String count = obj.getString("count");
// 交易结果: 0 – 交易成功 1 – 交易失败
String result = obj.getString("result");
// 交易类型
String transtype = obj.getString("transtype");
// 交易时间
String transtime = obj.getString("transtime");
// 商户私有信息
String cpprivate = obj.getString("cpprivate");
// 支付方式支付方式(该字段值后续可能会增加) 支付方式(该字段值后续可能会增加) 支付方式(该字段值后续可能会增加) 支付方式(该字段值后续可能会增加) 支付方式(该字段值后续可能会增加) 支付方式(该字段值后续可能会增加) 支付方式(该字段值后续可能会增加)
// 0 –话费支付
// 1 - 充值卡
// 2 –游戏点卡
// 3 –银行卡
// 401 –支付宝
// 402 –财付通
// 5 –联想 币
// 6 –联想 一键支付 一键支付
// 8、测试验证与发布集
String paytype = obj.getString("paytype");
logger.info("获取参数信息:exorderno="
+ exorderno
+ ",transid="
+ transid
+ ",appid="
+ appid
+ ",waresid="
+ waresid
+ ",feetype="
+ feetype
+ ",money="
+ money
+ ",count="
+ count
+ ",result="
+ result
+ ",transtype="
+ transtype
+ ",transtime="
+ transtime
+ ",cpprivate="
+ cpprivate
+ ",paytype="
+ paytype);
int results = Integer.parseInt(result);
if (results == 0) {
int chargeStatus = MoneyLog.CHARGE_FAILED;
// 步骤1:查询订单,依据业务流程,使用服务器申请的本公司的订单号查询订单,没有通过断言退出
MoneyLog moneyLog = moneyLogService.getmoneyLogByorderid(exorderno);
// 步骤2:通过订单记录取得渠道id,没有通过断言退出
ChargeChannel channel = Configuration.getChannel(moneyLog.getCid() + "");
Assert.notNull(channel, "渠道不存在:" + exorderno);
Assert.notNull(moneyLog, "订单号不存在:" + exorderno);
// 步骤3:通过渠道信息取得验证使用的加密私钥
String key = channel.getSeckey();
// 步骤4:验证签名
boolean validSign = validSign(transdata, sign, key);
Assert.isTrue(validSign, "lenovo验证签名失败!");
int money = 0;
if (MoneyLog.CHARGE_SUCCESS != moneyLog.getChargestatus()) {
// 步骤5:依据业务转换人名币to游戏币
money = GameUtil.parseMoney(Double.valueOf(money), channel.getCtype());
if (money > 0) {
// 1:已发送请求。2:充值成功。3:充值失败。
chargeStatus = MoneyLog.CHARGE_SUCCESS;
// 步骤6:通知服务器更新用户游戏币
// 返回给服务器的内容
this.pushMessage.PushServer(moneyLog.getGame(), moneyLog.getRid(), moneyLog.getAid(), money, channel.getChannel());
} else {
// 充值金额为0 定义为失败
chargeStatus = MoneyLog.CHARGE_FAILED;
}
// 步骤7:更新数据库,记录日志
moneyLog.setSpstatus("success");
moneyLog.setSporderid(transid);
moneyLog.setChargestatus(chargeStatus);
moneyLog.setAmount(Double.valueOf(money));
moneyLog.setMoney(money);
moneyLog.setUtime(new Date());
moneyLogService.saveOrUpdateMoneyLog(moneyLog);
logger.info("充值成功。。。。。。");
} else {
logger.info("已经充过了。。。。。。");
}
writer = resp.getWriter();
writer.write("success");
writer.flush();
writer.close();
} else {
logger.info("LENOVO充值失败。。。。。。");
}
} catch (Exception e) {
writer = resp.getWriter();
writer.write("FAIL");
writer.flush();
writer.close();
logger.error("lenovo充值异常:", e);
}
}
/**
* 验证签名
*
* @param transdata
* 同步过来的transdata数据
* @param sign
* 同步过来的sign数据
* @param key
* 应用的密钥(商户可从商户自服务系统获取)
* @return 验证签名结果 true:验证通过 false:验证失败
*/
public static boolean validSign(String transdata, String sign, String key) {
try {
String md5Str = MD5.md5Digest(transdata);
String decodeBaseStr = Base64.decode(key);
String[] decodeBaseVec = decodeBaseStr.replace('+', '#').split("#");
String privateKey = decodeBaseVec[0];
String modkey = decodeBaseVec[1];
String reqMd5 = RSAUtil.decrypt(sign, new BigInteger(privateKey), new BigInteger(modkey));
if (md5Str.equals(reqMd5)) {
return true;
} else {
return false;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}