之前在一家游戏公司写过游戏服务端,游戏支付功能,这个是接入“易接”平台的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"));
}
}