淘宝API开发ISV订购页面必看

在开发阿里软件的ISV应用时,你可能需要开发一个订购页面来让用户订购你的ISV应用。
对于开发思路,我在这里通过用户订购的流程来说明一下,估计可能会好理解一些。
1、用户点出购买,购买请求会提交到阿里软件平台。
2、阿里软件平台根据用户购买的情况配置参数,传到你的ISV。
3、ISV根据参数判断是新订、续订还是资源购买来跳转到相应的页面。(这里就需要你开发一个action,来接收参数并进行判断。还需要开发相当的页面)
4、用户选择了订购的内容后,由ISV组织参数(主要是内容的价格等),将这些参数传递给阿里平台。(这里又需要一个action)
5、用户在阿里平台进行付款,阿里平台转到支付宝平台(这一步无需我们干预)
6、付款成功后,支付宝平台将交易信息传递给阿里软件平台。(也无需我们干预)
7、阿里软件将本次交易的明细和结果通知给ISV,isv进行处理(这需要我们定义的一action来接收参数)

在上架之前要设置价格策略:如下页面
[img]http://forum.alisoft.com/attachments//day_080730/20080730_770414c72bfd66ec4ef7pHWjDDJmEDfF.jpg[/img]
其中有两个地址很关键:
1、软件价格描述地址:是一个订购url,此url的作用是进行巡辑判断,以进入不同的订购页面,如新订页面、续订页面、资源购买等。相当于我们上面所说的第3条,这个url具体到一个action。
2、通知url地址:此url的作用是接收平台发送过来的订购业务通知。相当于我们上面所说的第7条的action


明白了上面所说的,下面是java版本的开发订购的一个例子:
1、订购页面开发所需要的接口:IOrderConstanct

public interface IOrderConstanct {
public static final String PARAMETER_SIGNATURE="signature"; //签名
public static final String PARAMETER_SUBSCTYPE="subscType" ; //订购类型
public static final String PARAMETER_APPID="appId" ; //所订购的软件id
public static final String PARAMETER_APPEND="appEnd" ; //软件服务的截止时间
public static final String PARAMETER_GMTSTART="gmtStart"; //订单开始时间
public static final String PARAMETER_SUBSCEND="subscEnd" ; //订购控制记录的结束时间
public static final String PARAMETER_CTRLPARAMS="ctrlParams" ; //控制参数
public static final String PARAMETER_RETURNURL="returnUrl" ; //订购页面参数回传地址
public static final String PARAMETER_POSTDATA="https://www.it610.com/article/postData" ; //订购页面要原样回传的参数
public static final String PARAMETER_CODE="4df0c2b038f511ddbba29f5366f82354"; //注册时获得的安全码
public static final String PARAMETER_SIGN="sign";
public static final String PARAMETER_APPINSTANCEID="appInstanceId"; //应用实例ID
public static final String PARAMETER_EVENT="event"; //**类型,新订、续订、资源订购及退订
public static final String PARAMETER_USERID="userId"; //用户ID
public static final String PARAMETER_SUBSCID="subscId"; //订单ID
public static final String PARAMETER_GMTEND="gmtEnd"; //订单结束时间
public static final String PARAMETER_TOTALAMOUNT="totalAmount"; //订单总金额
public static final String PARAMETER_AMOUNT="amount"; //实付金额
public static final String PARAMETER_RENTAMOUNT="rentAmount"; //月租额
public static final String PARAMETER_RESOURCEAMOUNT="resourceAmount"; //购买资源金额
public static final String PARAMETER_COUPONAMOUTN="couponAmount"; //红包

}


2、下面开发软件价格描述地址所指的action,接收平台传过来的参数。假如我的地址是这样填写的http://127.0.0.1:8080/demoproj/order.do,这个action就是OrderAction。
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import com.alisoft.sip.sdk.isv.SignatureUtil;
import //导入上面定义的接口IOrderConstanct所在的包


public class OrderAction extends Action {

/**
* 当用户点击您的应用,要开通订购时,平台会向这个OrderAction post许多信息,在这个action中要做的就是接收这些参数,并根据参数进行判断是新订、续订、还是资源订购,并输出到具体页面
* Method execute
* @param mapping
* @param form
* @param request
* @param response
* @return ActionForward
*/
@SuppressWarnings("unchecked")
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
//接收所有从平台传递过来的参数
@SuppressWarnings("unused")
Enumeration en = request.getParameterNames();
Map map = new HashMap();
while(en.hasMoreElements()){//将传入的参数全部放在map中
String key =(String) en.nextElement();
map.put(key, request.getParameter(key));
}
String sig = (String) map.get(IOrderConstanct.PARAMETER_SIGNATURE); //因为加密时会去掉这个参数,所以先进行保存
String sign = SignatureUtil.Signature(map, (String) map.get(IOrderConstanct.PARAMETER_CODE)); //进行签名

HttpSession session = request.getSession();
session.setAttribute("signature", sig); //将加密文本放入session
session.setAttribute("subscType", map.get(IOrderConstanct.PARAMETER_SUBSCTYPE)); //订购资源类型
session.setAttribute("appId", map.get(IOrderConstanct.PARAMETER_APPID));
session.setAttribute("appInstanceId",map.get(IOrderConstanct.PARAMETER_APPINSTANCEID) );
session.setAttribute("appEnd", map.get(IOrderConstanct.PARAMETER_APPEND));
session.setAttribute("subscEnd", map.get(IOrderConstanct.PARAMETER_SUBSCEND));
session.setAttribute("ctrlParams", map.get(IOrderConstanct.PARAMETER_CTRLPARAMS));
session.setAttribute("postdata", map.get(IOrderConstanct.PARAMETER_POSTDATA)); //订购页面要原样回传的参数
session.setAttribute("returnUrl", map.get(IOrderConstanct.PARAMETER_RETURNURL)); //订购页面参数回传地址
session.setAttribute("gmtStart", map.get(IOrderConstanct.PARAMETER_GMTSTART)); //订单开始时间

/*
* 先验证应用ID及签名是否一致,然后根据订购类型跳转到不同的页面(0-新订、1、2-续订、3-资源订购)
*/
if(map.get(IOrderConstanct.PARAMETER_APPID).equals("1840")&&sig.equals(sign)){ //验证应用ID,这里要换成你的应用ID
if("0".equals(map.get(IOrderConstanct.PARAMETER_SUBSCTYPE))){//转到新订页面
return mapping.findForward("subsc");
}else if("1".equals(map.get(IOrderConstanct.PARAMETER_SUBSCTYPE))){//转到未到期续订页面
return mapping.findForward("neworder");
}else if("2".equals(map.get(IOrderConstanct.PARAMETER_SUBSCTYPE))){ //转到到期续订页面
return mapping.findForward("timeoutorder");
}else if("3".equals(map.get(IOrderConstanct.PARAMETER_SUBSCTYPE))){//转到资源订购页面
return mapping.findForward("buyorder");
}
return mapping.findForward("error"); //如果出错了,转到错误处理页面
}else{
return mapping.findForward("error");
}
}
}


3、我们开发一个订购页面。在这里,我们只开发一个页面作为说明: (在你开发时可能中气情况要有几个页面)





无标题文档 - 锐客网


























资源套餐一 10元
资源套餐二 20元
资源套餐三 30元
月租






注:此页面是内嵌在阿里平台的页面中的。图中虚线以下就是我们自己做的订购页面,虚线以上是平台提供的页面。如图:
[img]http://forum.alisoft.com/attachments//day_080730/20080730_e0083c374b751dbb9a0azRALD8d1KVgs.jpg[/img]
4、因为平台是内嵌了ISV提供的订购页面,所以,当用户选择好订购参数后,应用要把用户的订购参数回传给平台。我们在上面的代码中可以看到,form中的actoin写的是orderback.do,它是映射到orderbackAction的。当用户提交参数后,orderbackAction将获得订购参数。orderbackAction根据订购类型把信息回传给平台,并跳转回平台。OrderbackAction代码如下:

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import com.alisoft.sip.sdk.isv.SignatureUtil;
import com.constanct.*;

public class OrderbackAction extends Action {

public static java.text.SimpleDateFormat TIME_FORMATER = new SimpleDateFormat("yyyy-MM-dd"); //时间格式
private String postData; //平台要求的原样回传的参数
private String returnUrl; //回传url
private String subscType; //订购类型
private double amount; //金额
private double rentAmount;
private double resourceAmount;
private String ctrlParams; //控制参数
private String signature; //签名
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
/*
* 从servletcontext中读取需要的参数
*/
subscType=(String)request.getSession().getAttribute("subscType"); //订购类型
postData=https://www.it610.com/article/(String)request.getSession().getAttribute("postdata"); // 订购页面要原样回传的参数
returnUrl=(String)request.getSession().getAttribute("returnUrl"); //订购页面参数回传地址
String gmtStart=(String)request.getSession().getAttribute("gmtStart"); //订单开始时间
String gmtEnd=addMon(gmtStart,1); //计算订单结束时间,即订单开始时间加上订购时间,此处写死为一个月,但可在订购页面中让用户自行选择订购时间
/*
* 订购类型不同时,传给平台的参数也是不同的。所以,根据订购类型,分别进行参数的组织
*/
Map map=new HashMap();
if("0".equals(subscType)){//新订
map.put("postData", postData); //原样传回的参数
map.put("gmtStart",gmtStart); //开始时间
map.put("gmtEnd", gmtEnd); //结束时间
rentAmount=Integer.parseInt(request.getParameter("rent")); //页面传递过来的参数,按时间判断订购金额
resourceAmount=Integer.parseInt(request.getParameter("buy")); //页面传递过来的参数,按套餐判断订购金额
map.put("rentAmount", rentAmount);
map.put("resourceAmount", resourceAmount);
amount=rentAmount+resourceAmount; //将金额相加
map.put("amount", amount);
ctrlParams="amount=10&rent=50"; //这地方什么意思,说是控制参数,到现在没看懂,数字是写死的吗?估计不是写死的。可amount一定会比rent要大,结果这里却是小于。如果有看懂的请留言
map.put("ctrlParams", ctrlParams);
}else if("1".equals(subscType)){//未到期续订,不能修改订购开始时间,及控制参数
map.put("postData", postData);
map.put("gmtEnd", gmtEnd);
rentAmount=Integer.parseInt(request.getParameter("rent"));
resourceAmount=Integer.parseInt(request.getParameter("buy"));
map.put("rentAmount", rentAmount);
map.put("resourceAmount", resourceAmount);
amount=rentAmount+resourceAmount;
map.put("amount", amount);
}else if("2".equals(subscType)){//到期续订
map.put("postData", postData);
map.put("gmtStart",gmtStart);
map.put("gmtEnd", gmtEnd);
rentAmount=Integer.parseInt(request.getParameter("rent"));
resourceAmount=Integer.parseInt(request.getParameter("buy"));
map.put("rentAmount", rentAmount);
map.put("resourceAmount", resourceAmount);
amount=rentAmount+resourceAmount;
map.put("amount", amount);
ctrlParams="amount=10&rent=50"; //??
map.put("ctrlParams", ctrlParams);
}else {//订购资源,其中月租部分为零
map.put("postData", postData);
resourceAmount=Integer.parseInt(request.getParameter("buy"));
map.put("resourceAmount", resourceAmount);
map.put("rentAmount", 0);
map.put("amount", resourceAmount);
ctrlParams="amount=10&rent=50"; //??
map.put("ctrlParams", ctrlParams);
map.put("description", "中文");
}
signature=SignatureUtil.Signature(map, DemoConstant.PARAMETER_CODE); //签名
map.put("signature", signature);
/*
* 组织参数
*/
StringBuffer buffer = new StringBuffer();
boolean notFirst = false;
for (Map.Entry entry : map.entrySet()) {
if (notFirst) {
buffer.append("&");
} else {
notFirst = true;
}
Object value = https://www.it610.com/article/entry.getValue();
buffer.append(entry.getKey()).append("=").append(
encodeURL(value) );
}
String queryString=buffer.toString();

/*
* 跳转回平台,并带上相关的订购参数
*/
try{
response.sendRedirect(returnUrl+"?"+queryString);
}catch(Exception e){
e.printStackTrace();

}
return null;
}

/*
* 编码
*/
private String encodeURL(Object target) {
String result = (target != null) ? target.toString() : "";
try {
result = URLEncoder.encode(result, "GBK");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return result;
}

/*
* 日期计算
*/
public static String addMon(String s, int n) {
Calendar cd=null;
try {
cd = Calendar.getInstance();
cd.setTime(TIME_FORMATER.parse(s));
cd.add(Calendar.MONTH, n); //增加一月
cd.add(Calendar.DATE, -1);
} catch (Exception e) {
e.printStackTrace();
}

return TIME_FORMATER.format(cd.getTime());
}

}

跳转回平台后,出现如下页面:[img]http://forum.alisoft.com/attachments//day_080730/20080730_b9086aab8ebe85c72f65kLur3QK6S4cl.jpg[/img]
5、点击付款后,会跳到支付宝支付中心,用户进行付款。付款成功后,平台会发送相关信息给通知url。这又是一action,用于接收平台传递过来的交易情况的信息:
package com.order.struts.action;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import com.alisoft.sip.sdk.isv.SignatureUtil;
import //导入上面IOrderConstanct所在的包
public class InformAction extends Action {

private String sign;
@SuppressWarnings("unchecked")
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response){
Enumeration e=(Enumeration)request.getParameterNames(); //获得平台传过来的订购付款信息
Map map = new HashMap();
ServletContext context = this.servlet.getServletContext();
context.removeAttribute("gmtStart");
while(e.hasMoreElements()){
String params=(String)e.nextElement();
map.put(params, request.getParameter(params));
context.setAttribute(params, request.getParameter(params));
}
String sig=(String)map.get(DemoConstant.PARAMETER_SIGNATURE);
sign=SignatureUtil.Signature(map, DemoConstant.PARAMETER_CODE); //根据得到的参数进行自签名
if(sign.equals(sig)){//验证签名
if(map.get(DemoConstant.PARAMETER_EVENT).equals("subsc")){//新订
/*
*添加订购关系;
*添加月租信息,资源信息;
*建立账户信息,账户充值;
*/
}else if(map.get(DemoConstant.PARAMETER_EVENT).equals("renewAhead")){
/*
* 未到期续订,修改月租信息,资源信息
*/
}else if(map.get(DemoConstant.PARAMETER_EVENT).equals("renew")){
/*
* 到期续订,修改月租信息,资源信息
*/
}else if(map.get(DemoConstant.PARAMETER_EVENT).equals("resource")){
/*
* 订购资源,修改资源信息
*/
}else if(map.get(DemoConstant.PARAMETER_EVENT).equals("break")){
/*
* 退订,删除订购关系,删除账户信息,释放资源
*/
}else{
/*
* error
*/
}
}
return null;
}
}

至此,我们的收费页面开发就完成了。 :cry: :cry: :cry:

    推荐阅读