之前在一家游戏公司写过游戏服务端,游戏支付功能,这个是接入“易接”平台的SDK实现的,
由于涉及的坑比较多,所以总结一下经验,以及奉上完整代码。
首先我们的项目是以Cocos2dx引擎的手游,这个用visual Studio编写代码,
这个项目是LUA工程,但是里面有多个平台的代码,但是我们现在只写Android这一块
它可以实现跨平台:Android,IOS,blackberry,Linux,marmalade,window平台上跑,
Lua工程代码可以像swift 的编译器那样,写完即刻运行,更新的时候,不需要重新下载一个包,
打补丁更新!维护起来很方便
由于我是写服务端,这一块,所以我简单陈述这个实现的过程,
首先网游肯定有充值的功能,我现在通过接入第三方的SDK 接口,实现这个支付的功能
易接SDK 帮助文档
https://www.1sdk.cn/helpcenter.html
一、首先我们需要精确的定位!
此操作编译软件:Eclipse_SDK
我们是一个网游,我们的项目是Android,最终的目的是完成一个支付的功能
先选择客户端,只有客户端的接口完善了才会发送请求到服务端验证,才会同步信息!
既然选择了Android,我的代码无疑是JAVA
为什么不选择C++呢?cocos2dx 不是C++吗?已经说了,这里是一个Android的项目
二、下载官方的参考文档 demo 和 sdk包
这个是生成渠道包的软件,就是写好接口后,在通过其他平台接入扩展功能
游戏客户端 → 游戏服务端 → 易接服务端 → 其他渠道服务端
写好接口代码,找一个渠道服务端 注册申请开发者,
用 易接的PC端打包的APK,key在渠道服务端 这个获取,比如我注册“乐视/联想”的平台,然后上传app,可以获取key,打包后的apk,运行可以接入接口。
三、我们打开下载的SDK包,
里面有很详细的内容,包括PDF文档,跟网页端的一样,只是稍微代码规范一点
资源包自己根据文档进行整理,该打上那些已经说的很详细了
demo有个易接的apk参考,文件夹里面是代码,
可以用ec导入,仔细查看
四、步骤流程:
我大体上的思路是:
1、在onCreate()创建窗口加载游戏的时候,初始化SDK ——initSDK();
并且写上登录监听!
SFOnlineHelper.setLoginListener(activity, newSFOnlineLoginListener() {
@Override
public void onLoginSuccess(SFOnlineUser user, Object customParams) {
//登陆成功回调
}
@Override
public void onLoginFailed(String reason, Object customParams) {
//登陆失败回调
}
@Override
public voidon Logout(Object customParams) {
//登出回调
}
});
2、在onStart()方法中,使用登录方法 SFOnlineHelper.login(activity, "Login");
监听到登录后,跳转登录验证
public void onLoginSuccess()
3、登录验证后,创建角色,验证角色
setRoleData(Context context, String roleId, String roleName, String roleLevel, String zoneId, String zoneName){
}
4、角色OK后,写支付的接口——定额计费,非定额计费
pay (Context context, intunitPrice, String unitName, int count, String callBackInfo, String callBackUrl,SFOnlinePayResultListenerpayResultListener){
}
5、扩展接口暂时不用写
注意:只有退出才清空内容 System.exit(0); ,
或许你打开原来的项目会说:
1.wtf,这不是继承Activity,不是FragmentActivity,而是
extends Cocos2dxActivity ?
2、wtf,这游戏项目没有xml页面?
3、退出按钮不听话了?游戏无法退出?无法监听按键?
这跟demo不一样啊!
该什么说呢?其实游戏页面只有一个activity,所有的页面都在这里显示
如果遇到闪退:1、登录验证失败,回退 2、代码报错 3、6.0的权限问题,降低为4.3 这样
五、完整代码公布:
1、LuaTest.java 主activity
(因为我只是一只菜鸟,所以狂打 log日志跟踪)
package com.android.zdhsoft;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import com.snowfish.cn.ganga.helper.SFOnlineExitListener;
import com.snowfish.cn.ganga.helper.SFOnlineHelper;
import com.snowfish.cn.ganga.helper.SFOnlineLoginListener;
import com.snowfish.cn.ganga.helper.SFOnlinePayResultListener;
import com.snowfish.cn.ganga.helper.SFOnlineUser;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.FrameLayout;
import android.widget.Toast;
import org.cocos2dx.lib.Cocos2dxActivity;
import org.cocos2dx.lib.Cocos2dxGLSurfaceView;
import org.json.JSONException;
import org.json.JSONObject;
import utils.LoginHelper;
public class luaTest extends Cocos2dxActivity {
private static final String TAG = "--luaTest--";
FrameLayout mViewContainer;
String tag = null;
static LoginHelper helper = null;
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "——————luaTest启动,onCreate——————");
super.onCreate(savedInstanceState);
//登录监听
loginListener();
Log.d(TAG, "——————启动登录监听,loginListener——————");
// TODO 初始化易接sdk 不带初始化监听的接口
SFOnlineHelper.onCreate(this);
Log.d(TAG, "——————初始化SDK,onCreate——————");
}
public Cocos2dxGLSurfaceView onCreateGLSurfaceView() {
return new Cocos2dxGLSurfaceView(this);
}
static {
System.loadLibrary("cocos2dlua");
}
@Override
public void onStart() {
super.onStart();
Log.d(TAG, "——————开始启动游戏——————");
//这里 模拟一下 调用登录
SFOnlineHelper.login(this, "Login");
Log.d(TAG, "——————登录游戏,login——————");
}
@Override
public void onPause() {
super.onPause();
Log.d(TAG, "——————暂停游戏——————");
SFOnlineHelper.onPause(this);
//除非退出游戏否则绝对不能调用 !!!!
// System.exit(0);
}
public void onStop() {
super.onStop();
Log.d(TAG, "——————停止游戏——————");
SFOnlineHelper.onStop(this);
}
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "——————重新开始游戏——————");
SFOnlineHelper.onResume(this);
}
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "——————销毁游戏——————");
SFOnlineHelper.onDestroy(this);
}
/* protected void onRestart() {
Log.d(TAG, "——————重新启动游戏——————");
SFOnlineHelper.onRestart(this);
}*/
//登录的监听方法
public void loginListener(){
SFOnlineHelper.setLoginListener(this, new SFOnlineLoginListener(){
@Override
public void onLoginSuccess(SFOnlineUser user, Object customParams) {
/**
* 获取用户信息 传到你们的游戏内,绑定游戏玩家 用户的唯一标示是 channneluserid
* 用户唯一标示 --- 该玩家在 该渠道 的唯一标示。若要实现全渠道 唯一 请自行处理映射关系 建议 使用 {sdk_userID}
*/
String userID = user.getChannelUserId();
String app = user.getProductCode();
String sdk = user.getChannelId();
String token = user.getToken();
String userName = user.getUserName(); // 这个参数是 可选的 不是用户唯一标示,通常建议不用。
System.out.println("————"+"userID:"+userID+",app:"+app+",sdk:"+sdk +",token"+token+",userName:"+userName);
// 用户信息获取结束
//登录验证
try {
if(helper != null){
helper.setOnlineUser(user);
}
LoginCheck(user);
Toast.makeText(getContext(), "验证:账户登录成功", 3000).show();
} catch (JSONException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
@Override
public void onLoginFailed(String reason, Object customParams) {
Log.d(TAG, "——————登录失败!——————");
Toast.makeText(getContext(), "验证:账户登录失败", 3000).show();
}
@Override
public void onLogout(Object customParams) {
Log.d(TAG, "——————登出成功!——————");
Toast.makeText(getContext(), "验证:登出成功", 3000).show();
}
});
}
//登出
public void onlogout(Object customParams){
Log.d(TAG, "——————登出游戏——————"+ customParams);
SFOnlineHelper.logout(this, "LoginOut");
Toast.makeText(this, "账户登出", Toast.LENGTH_LONG).show();
/*if(helper!=null){
helper.setOnlineUser(null);
helper.setLogin(false);
helper.getHandler(this).postDelayed(new Runnable(){
@Override
public void run() {
SFOnlineHelper.login((Activity) getContext(), "Login");
Log.d(TAG, "——————登出游戏,切换账号——————");
}
}, 200);
}*/
}
//登录验证
public void LoginCheck(final SFOnlineUser user) throws JSONException, UnsupportedEncodingException{
Log.d(TAG, "——————登录验证!——————");
//此处进行的登录验证是 模拟验证
// 正确的验证流程是 游戏客户端 ----》游戏服务器----》易接服务器---》渠道服务器
// 这里就不做了 留个 空
//TODO 实现真实的验证
String url = "http://192.168.0.105:8000/run"+createLoginURL(user);
if (url == null)
return;
System.out.println("url:"+url);
new Thread(new Runnable(){
@Override
public void run() {
MainActivity.SendErrorToGMServer(user.getProductCode(), user.getChannelId(), user.getToken(), user.getChannelId());
System.out.println("————1、发送登录验证到本地服务器————");
}
}).start();
String result = LoginHelper.executeHttpGet(url);
Log.e(TAG,"————发送信息成功————"+result);
if(result==null ||!result.equals("SUCCESS")){
if(helper != null){
helper.setLogin(false);
}
LoginHelper.showMessage("未登录", this);
}else{
if(helper != null){
helper.setLogin(true);
}
}
// 通常在游戏内调用 上传角色信息的接口 这里模拟设置角色信息
setRole();
// 这些做完就接好了 用户 系统 下面是支付 通常是用户触发
pay();
onBackPressed();
/* Toast.makeText(getContext(), "退出!", 3000).show(); */
}
//模拟设置角色基本信息
@SuppressWarnings("unused")
private void setRole() throws JSONException{
SFOnlineHelper.setRoleData(this, "1","骑士", "100", "1", "阿狸一区");
Log.d(TAG, "——————角色初始化——————");
// setData key --? enterserver levelup createrole 最常见的三个场景。
// info 用户信息 参考 demo的json
JSONObject info = new JSONObject();
info.put("roleName", "saber"); //当前登录的玩家角色ID,必须为数字
info.put("roleId", "775522233"); //当前登录的玩家角色名,不能为空,不能为null
info.put("roleLevel", "100"); //当前登录的玩家角色等级,必须为数字,且不能为0,若无,传入1
info.put("zoneId", "1"); //当前登录的游戏区服ID,必须为数字,且不能为0,若无,传入1
info.put("zoneName", "阿狸一区"); //当前登录的游戏区服名称,不能为空,不能为null
info.put("balance", "0"); //用户游戏币余额,必须为数字,若无,传入0
info.put("vip", "1"); //当前用户VIP等级,必须为数字,若无,传入1
info.put("partyName", "无帮派"); //当前角色所属帮派,不能为空,不能为null,若无,传入“无帮派”
info.put("roleCTime", "234234");//单位为秒,创建角色的时间
info.put("roleLevelMTime", "54456556");//单位为秒,角色等级变化时间
//实现完整角色验证
SFOnlineHelper.setData(this, "key", info.toString()); //一定要 toString !
LoginHelper.showMessage("角色saber,登录验证成功!", this);
Log.d(TAG, "——————角色验证成功!——————");
}
//传递地址
private String createLoginURL(SFOnlineUser user) throws UnsupportedEncodingException{
StringBuilder builder = new StringBuilder();
builder.append("?app=");
builder.append(URLEncoder.encode(user.getProductCode(), "utf-8"));
builder.append("&sdk=");
builder.append(URLEncoder.encode(user.getChannelId(), "utf-8"));
builder.append("&uin=");
builder.append(URLEncoder.encode(user.getChannelUserId(), "utf-8"));
builder.append("&sess=");
builder.append(URLEncoder.encode(user.getToken(), "utf-8"));
return builder.toString();
}
// 退出
@Override
public void onBackPressed() {
Log.d(tag, "————onBackPressed,退出游戏————");
// exit方法用于系统全局退出
SFOnlineHelper.exit(this, new SFOnlineExitListener() {
@Override
public void onSDKExit(boolean bool) {
if (bool) {
// apk退出函数,demo中也有使用System.exit()方法;但请注意360SDK的退出使用exit()会导致游戏退出异常
finish();
}
}
/*
* onNoExiterProvide
*
* @description SDK没有退出方法及界面,回调该函数,可在此使用游戏退出界面
*/
@Override
public void onNoExiterProvide() {
AlertDialog.Builder builder = new Builder(luaTest.this);
builder.setTitle("是否要退出游戏界面");
builder.setPositiveButton("退出",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
luaTest.this.finish();
System.exit(0);
}
});
builder.show();
}
});
}
//定额计费接口
@SuppressWarnings("unused")
private void pay(){
int unitPrice = 10 ; //游戏道具价格,单位为 分
String itemName="钻石"; //虚拟货币名称
int count= 1; //默认道具数量
String callBackInfo="12345678910"; //自定义的说明,判断交易详细内容,不要用空格和特殊字符; 暂时设定为订单ID
String callBackUrl = "http://123.57.12.130:8000/paycallback" ; // arg5 ,将交易结果传给服务器的通知url,可看到explain的信息
// arg6透传参数 不要有汉字空格,可自定义
try{
SFOnlineHelper.pay(this, unitPrice, itemName, count, callBackInfo,callBackUrl, new SFOnlinePayResultListener() {
@Override
public void onSuccess(String remain) {
Toast.makeText(getContext(), "支付成功,获得1钻石", 2000).show();
Log.d(TAG,"—————支付成功———————");
}
@Override
public void onFailed(String remain) {
// 支付失败
Log.d(TAG,"—————支付失败———————");
Toast.makeText(getContext(), "错误信息,支付失败!", 2000).show();
}
@Override
public void onOderNo(String orderNo) {
// 订单创建成就会回调通常记录到服务器
Log.e(TAG, "————订单创建,发生到服务器");
LoginHelper.showMessage("购买钻石订单号:" + orderNo, getContext());
}
});
}catch(Exception e){
e.printStackTrace();
Log.e(TAG, "————错误,没有进入支付!————");
}
}
//非定额计费接口
@SuppressWarnings("unused")
private void charge(int price){
if(!LoginHelper.instance().isLogin()){
Toast.makeText(this,"用户未登陆",Toast.LENGTH_SHORT).show();
return ;
}
Log.e(TAG, "—————非定额计费—————");
String itemName="钻石"; //虚拟货币名称
int unitPrice = 10 ; //游戏道具价格,单位为 分
int count= 1; //默认道具数量
String callBackInfo="123456"; //自定义的说明,判断交易详细内容,不要用空格和特殊字符,暂时设置为订单ID
String callBackUrl = "http://123.57.12.130:8000/paycallback" ; // arg5 ,将交易结果传给服务器的通知url,可看到explain的信息
// arg6透传参数 不要有汉字空格,可自定义
try{
SFOnlineHelper.charge(this,itemName,unitPrice, count,callBackInfo, callBackUrl, new SFOnlinePayResultListener() {
@Override
public void onSuccess(String remain) {
Toast.makeText(getContext(), "支付成功,获得10钻石,5黄金,20积分", 2000).show();
System.out.println("—————支付成功———————");
}
@Override
public void onFailed(String remain) {
// 支付失败
Toast.makeText(getContext(), "错误信息,支付失败!", 2000).show();
}
@Override
public void onOderNo(String orderNo) {
LoginHelper.showMessage("订单号:" + orderNo, getContext());
}
});
}catch(Exception e){
e.printStackTrace();
Log.e(TAG, "————错误,没有进入定额支付!————");
}
}
}
class LuaGLSurfaceView extends Cocos2dxGLSurfaceView {
public LuaGLSurfaceView(Context context) {
super(context);
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
android.os.Process.killProcess(android.os.Process.myPid());
}
return super.onKeyDown(keyCode, event);
}
}
2、客户端(当前ec—sdk项目)请求发送/接收 服务端消息 activity
MainActivity :
package com.android.zdhsoft;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import com.android.zdhsoft.R;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
public class MainActivity extends Activity {
private static final String TAG ="--MainActivity--";
private static String GMSERVER_API = "http://192.168.0.105:8000/run"; //公司外网地址,这个请自己填写
Messenger mes;
boolean isBound;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "—————onCreate—————");
}
//登录发生请求
public void doLogin(View view) {
Intent intent = new Intent(MainActivity.this, luaTest.class);
startActivity(intent);
Log.d(TAG, "—————doLogin—————");
}
protected void onStart(){
super.onStart();
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG,"—————onStop—————");
}
public static void setErrorCollectApi(String api){
GMSERVER_API = api;
}
//多抛出异常
public static void SendErrorToGMServer(String app,String sdk,String token,String userName){
Log.d(TAG, "—————SendErrorToGMServer—————");
List<NameValuePair> params = new ArrayList<NameValuePair>();
try {
HttpPost post = new HttpPost(GMSERVER_API);
params.add(new BasicNameValuePair("app",app));
params.add(new BasicNameValuePair("sdk",sdk));
params.add(new BasicNameValuePair("token",token));
params.add(new BasicNameValuePair("userName",userName));
/* params.add(new BasicNameValuePair("sys_version",android.os.Build.VERSION.RELEASE));*/
params.add(new BasicNameValuePair("dev",android.os.Build.MODEL));
post.setEntity(new UrlEncodedFormEntity(params));
new DefaultHttpClient().execute(post);
System.out.println("—————异常一—————");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
Log.d(TAG, "—————异常一—————");
} catch (ClientProtocolException e) {
e.printStackTrace();
Log.d(TAG, "—————异常二—————");
} catch (IOException e) {
e.printStackTrace();
Log.d(TAG, "—————异常三—————");
}
Log.d(TAG, "—————SendErrorToGMServer完成—————");
}
}
3、AndroidManifest.xml
(仅供参考)
<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.zdhsoft"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="19"
android:targetSdkVersion="20" />
//权限
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-feature android:glEsVersion="0x00020000" />
-
<application
android:name="com.snowfish.cn.ganga.helper.SFOnlineApplication"
android:allowBackup="true"
android:icon="@drawable/aa"
android:label="@string/app_name" >
-//屏幕控制
<activity
android:name="activity.DemoMainActivity"
android:configChanges="orientation|keyboardHidden|navigation|screenSize"
android:label="@string/app_name"
android:launchMode="singleTop"
android:screenOrientation="sensor"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
<!-- <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> -->
</activity>
-
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
<!-- <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> -->
</activity>
<!-- android:screenOrientation="" landscape:横屏;portrait:竖屏;sensor:物理感应器 -->
-
<activity
android:name=".luaTest"
android:configChanges="orientation|keyboardHidden|navigation|screenSize"
android:label="@string/title_activity_main"
android:screenOrientation="sensor"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
-
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="view.PaymentView"
android:label="@string/title_activity_payment" >
</activity>
-
<service
android:name="com.snowfish.a.a.s.ABGSvc"
android:enabled="true"
android:process="com.snowfish.a.a.bg" >
-
<intent-filter>
<action android:name="com.snowfish.a.a.s.ABGSvc" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
<meta-data
android:name="com.snowfish.customer"
android:value="SNOWFISH" >
</meta-data>
<meta-data
android:name="com.snowfish.channel"
android:value="SNOWFISH" >
</meta-data>
<meta-data
android:name="com.snowfish.sdk.version"
android:value="2" >
</meta-data>
<!-- END SNOWFISH SDK -->
<!-- 此参数不做修改,保持默认就行,打包会自动替换 -->
<!-- com.snowfish.appid 游戏的唯一标识,用于区分不同游戏的唯一标准。在易接开发者中心游戏管理模块中创建新游戏获取 -->
<meta-data
android:name="com.snowfish.appid"
android:value="{4477D4AB-399F0338}" >
</meta-data>
<!-- com.snowfish.channelid 支付渠道标识,此id可区分渠道,在易接后台有相应的渠道对照表 -->
<meta-data
android:name="com.snowfish.channelid"
android:value="{4ff036a1-3254eafe}" >
</meta-data>
</application>
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true" />
</manifest>
其他修改请参考帮助文档!
六、服务端代码公布:
Eclipse,这个是另外一个ec,不是ec_sdk
1、BaseServlet.java
package com.test;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class BaseServlet extends HttpServlet{
// 应用在易接服务获取的同步密钥
private final String PRIVATE_KEY = "V2X0XE5DSZ4AGFXJCA5YZ5CDUQO4LPJY"; //自己在轨道SDK申请,我这个是乐视的
private final int LOGIN_RESULT_SUCCESS = 0;
private final String CHECK_LOGIN_URL = "http://sync.1sdk.cn/login/check.html";
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("BaseServlet.doGet()");
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("BaseServlet.doPost()");
PrintWriter w = resp.getWriter();
String app = req.getParameter("app");
String sdk = req.getParameter("sdk");
String uin = req.getParameter("token");
String sess = req.getParameter("userName");
StringBuilder getUrl = new StringBuilder();
getUrl.append(CHECK_LOGIN_URL);
getUrl.append("?app=");
getUrl.append(app);
getUrl.append("&sdk=");
getUrl.append(sdk);
getUrl.append("&uin=");
getUrl.append(URLEncoder.encode(uin, "UTF-8"));
getUrl.append("&sess=");
getUrl.append(URLEncoder.encode(sess, "UTF-8"));
try {
ServerLauncher.SimpleHTTPResult ret = ServerLauncher.simpleInvoke ("GET", getUrl.toString(), null, null);
// 下面的返回值由CP服务器和客户端定义,这里的返回值只做参考用
if (ret.code != 200) {
w.write("ERROR");
return;
}
if (ret.data == null || ret.data.length == 0) {
w.write("ERROR");
return;
} else {
String r = new String (ret.data);
Integer i = new Integer(r);
if (i == LOGIN_RESULT_SUCCESS) {
w.write("SUCCESS");
System.out.println("app:"+app);
System.out.println("sdk:"+sdk);
System.out.println("token:"+uin);
System.out.println("userName:"+sess);
System.out.println("————登录验证完成————");
} else {
w.write("ERROR");
System.out.println("————登录验证失败!————");
}
return;
}
} catch (Exception e) {
e.printStackTrace();
}finally{
w.flush();
w.close();
}
/*
* 以上登录验证流程完成!
* 以下是订单支付的接收信息
*/
String app2 = req.getParameter("app2");
String cbi = req.getParameter("cbi");
String ct = req.getParameter("ct");
String fee = req.getParameter("fee");
String pt = req.getParameter("pt");
String ssid = req.getParameter("ssid");
String st = req.getParameter("st");
String tcd = req.getParameter("tcd");
String uid = req.getParameter("uid");
String ver = req.getParameter("ver");
StringBuffer sbEnc = new StringBuffer ();
sbEnc.append ("app=");
sbEnc.append (req.getParameter("app"));
sbEnc.append ("&cbi=");
sbEnc.append (req.getParameter("cbi"));
sbEnc.append ("&ct=");
sbEnc.append (req.getParameter("ct"));
sbEnc.append ("&fee=");
sbEnc.append (req.getParameter("fee"));
sbEnc.append ("&pt=");
sbEnc.append (req.getParameter("pt"));
sbEnc.append ("&sdk=");
sbEnc.append (req.getParameter("sdk"));
sbEnc.append ("&ssid=");
sbEnc.append (req.getParameter("ssid"));
sbEnc.append ("&st=");
sbEnc.append (req.getParameter("st"));
sbEnc.append ("&tcd=");
sbEnc.append (req.getParameter("tcd"));
sbEnc.append ("&uid=");
sbEnc.append (req.getParameter("uid"));
sbEnc.append ("&ver=");
sbEnc.append (req.getParameter("ver"));
String sign = req.getParameter("sign");
boolean result = MD5.encode(sbEnc + PRIVATE_KEY).equalsIgnoreCase(sign);
if(result){
// 此处CP可以持久化消费记录
//st==1是支付成功,才能给用户发道具
// 操作成功后需要返回SUCCESS告诉易接服务器已经接收成功
w.write("SUCCESS");
System.out.println("开始验证订单!");
System.out.println("生成的订单信息:"+cbi+ct+fee+pt+ssid+st+tcd+uid+ver);
}else{
// 返回ERROR易接服务器会根据一定的策略重新同步消费记录给CP
w.write("ERROR");
System.out.println("开始验证订单失败!");
}
w.flush();
w.close();
}
}
2、ServerLauncher.java
package com.test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.nio.SelectChannelConnector;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHolder;
import org.mortbay.thread.BoundedThreadPool;
public class ServerLauncher {
private void launch(int port) throws Exception {
Server server = new Server();
BoundedThreadPool threadPool = new BoundedThreadPool();
threadPool.setMinThreads(50);
threadPool.setMaxThreads(1000);
server.setThreadPool(threadPool);
SelectChannelConnector connector = new SelectChannelConnector();
connector.setPort(port);
// server.addHandler(webContext);
server.addConnector(connector);
Context root = new Context(server, "/", 1);
root.addServlet(new ServletHolder(new BaseServlet()),"/run");
server.start();
}
public static void main(String[] args) throws Exception {
new ServerLauncher().launch(8000);
System.out.println("Start ");
}
//易接例子
public static class SimpleHTTPResult {
public int code;
public byte[] data;
}
public static SimpleHTTPResult simpleInvoke (String method, String url, String contentType, byte[] outdata) throws IOException {
SimpleHTTPResult res = new SimpleHTTPResult ();
HttpURLConnection http = (HttpURLConnection)(new URL (url)).openConnection ();
http.setRequestMethod (method);
if (contentType != null)
http.setRequestProperty ("Content-Type", contentType);
if (outdata != null) {
http.setRequestProperty ("Content-Length", Integer.toString (outdata.length));
}
http.setDoOutput (outdata != null ? true : false);
http.setDoInput (true);
http.connect ();
if (outdata != null) {
OutputStream outs = http.getOutputStream ();
outs.write (outdata);
outs.close ();
}
res.code = http.getResponseCode ();
if (res.code == 404) {
return res;
}
InputStream stream = http.getInputStream ();
try {
int len = http.getContentLength ();
byte[] data;
if (len >= 0) {
data = new byte[len];
int off = 0;
while (off < len) {
int read = stream.read (data, off, len - off);
if (read < 0)
throw new IOException ();
off += read;
}
} else {
ByteArrayOutputStream baos = new ByteArrayOutputStream ();
byte[] buffer = new byte[4096];
for (;;) {
int read = stream.read (buffer, 0, buffer.length);
if (read < 0)
break;
baos.write (buffer, 0, read);
}
baos.close ();
data = baos.toByteArray ();
}
res.data = data;
} finally {
stream.close ();
}
return res;
}
public static byte[] post (URL url, String contentType, byte[] outdata) throws IOException {
HttpURLConnection http = (HttpURLConnection)url.openConnection ();
http.setRequestProperty ("Content-Type", contentType);
http.setDoOutput (true);
http.setDoInput (true);
http.connect ();
OutputStream outs = http.getOutputStream ();
outs.write (outdata);
outs.close ();
int code = http.getResponseCode ();
if (code != 200) {
throw new IOException ("Bad RPC '" + url.toString () + "': " + code);
}
InputStream stream = http.getInputStream ();
try {
int len = http.getContentLength ();
byte[] data;
if (len >= 0) {
data = new byte[len];
int off = 0;
while (off < len) {
int read = stream.read (data, off, len - off);
if (read < 0)
throw new IOException ();
off += read;
}
} else {
ByteArrayOutputStream baos = new ByteArrayOutputStream ();
byte[] buffer = new byte[4096];
for (;;) {
int read = stream.read (buffer, 0, buffer.length);
if (read < 0)
break;
baos.write (buffer, 0, read);
}
baos.close ();
data = baos.toByteArray ();
}
return data;
} finally {
stream.close ();
}
}
}
3、MD5.java 这个加密用的
package com.test;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5 {
// 全局数组
private final static String[] strDigits = { "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
public MD5() {
}
// 返回形式为数字跟字符串
private static String byteToArrayString(byte bByte) {
int iRet = bByte;
// System.out.println("iRet="+iRet);
if (iRet < 0) {
iRet += 256;
}
int iD1 = iRet / 16;
int iD2 = iRet % 16;
return strDigits[iD1] + strDigits[iD2];
}
// 返回形式只为数字
private static String byteToNum(byte bByte) {
int iRet = bByte;
System.out.println("iRet1=" + iRet);
if (iRet < 0) {
iRet += 256;
}
return String.valueOf(iRet);
}
// 转换字节数组为16进制字串
private static String byteToString(byte[] bByte) {
StringBuffer sBuffer = new StringBuffer();
for (int i = 0; i < bByte.length; i++) {
sBuffer.append(byteToArrayString(bByte[i]));
}
return sBuffer.toString();
}
public static String encode(String strObj) {
String resultString = null;
try {
resultString = new String(strObj);
MessageDigest md = MessageDigest.getInstance("MD5");
// md.digest() 该函数返回值为存放哈希值结果的byte数组
resultString = byteToString(md.digest(strObj.getBytes()));
} catch (NoSuchAlgorithmException ex) {
ex.printStackTrace();
}
return resultString;
}
public static void main(String[] args) {
System.out.println(MD5.encode("000000"));
}
}