淘宝API开发ISV订购页面必看
程序员文章站
2022-07-15 14:16:35
...
在开发阿里软件的ISV应用时,你可能需要开发一个订购页面来让用户订购你的ISV应用。
对于开发思路,我在这里通过用户订购的流程来说明一下,估计可能会好理解一些。
1、用户点出购买,购买请求会提交到阿里软件平台。
2、阿里软件平台根据用户购买的情况配置参数,传到你的ISV。
3、ISV根据参数判断是新订、续订还是资源购买来跳转到相应的页面。(这里就需要你开发一个action,来接收参数并进行判断。还需要开发相当的页面)
4、用户选择了订购的内容后,由ISV组织参数(主要是内容的价格等),将这些参数传递给阿里平台。(这里又需要一个action)
5、用户在阿里平台进行付款,阿里平台转到支付宝平台(这一步无需我们干预)
6、付款成功后,支付宝平台将交易信息传递给阿里软件平台。(也无需我们干预)
7、阿里软件将本次交易的明细和结果通知给ISV,isv进行处理(这需要我们定义的一action来接收参数)
在上架之前要设置价格策略:如下页面
其中有两个地址很关键:
1、软件价格描述地址:是一个订购url,此url的作用是进行巡辑判断,以进入不同的订购页面,如新订页面、续订页面、资源购买等。相当于我们上面所说的第3条,这个url具体到一个action。
2、通知url地址:此url的作用是接收平台发送过来的订购业务通知。相当于我们上面所说的第7条的action
明白了上面所说的,下面是java版本的开发订购的一个例子:
1、订购页面开发所需要的接口:IOrderConstanct
2、下面开发软件价格描述地址所指的action,接收平台传过来的参数。假如我的地址是这样填写的http://127.0.0.1:8080/demoproj/order.do,这个action就是OrderAction。
3、我们开发一个订购页面。在这里,我们只开发一个页面作为说明: (在你开发时可能中气情况要有几个页面)
4、因为平台是内嵌了ISV提供的订购页面,所以,当用户选择好订购参数后,应用要把用户的订购参数回传给平台。我们在上面的代码中可以看到,form中的actoin写的是orderback.do,它是映射到orderbackAction的。当用户提交参数后,orderbackAction将获得订购参数。orderbackAction根据订购类型把信息回传给平台,并跳转回平台。OrderbackAction代码如下:
5、点击付款后,会跳到支付宝支付中心,用户进行付款。付款成功后,平台会发送相关信息给通知url。这又是一action,用于接收平台传递过来的交易情况的信息:
对于开发思路,我在这里通过用户订购的流程来说明一下,估计可能会好理解一些。
1、用户点出购买,购买请求会提交到阿里软件平台。
2、阿里软件平台根据用户购买的情况配置参数,传到你的ISV。
3、ISV根据参数判断是新订、续订还是资源购买来跳转到相应的页面。(这里就需要你开发一个action,来接收参数并进行判断。还需要开发相当的页面)
4、用户选择了订购的内容后,由ISV组织参数(主要是内容的价格等),将这些参数传递给阿里平台。(这里又需要一个action)
5、用户在阿里平台进行付款,阿里平台转到支付宝平台(这一步无需我们干预)
6、付款成功后,支付宝平台将交易信息传递给阿里软件平台。(也无需我们干预)
7、阿里软件将本次交易的明细和结果通知给ISV,isv进行处理(这需要我们定义的一action来接收参数)
在上架之前要设置价格策略:如下页面
其中有两个地址很关键:
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="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<String,Object> map = new HashMap<String, Object>(); 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、我们开发一个订购页面。在这里,我们只开发一个页面作为说明: (在你开发时可能中气情况要有几个页面)
<%@ page contentType="text/html; charset=gb2312" language="java" import="java.sql.*" errorPage="" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> <title>无标题文档</title> </head> <body> <form action="./orderback.do" method="OST"> <%--request.getSession().setAttribute("a",request.getSession().getAttribute("a"));--%> <%--out.println(application.getAttribute("b")) ;--%> <table width="400" border="1"align="center"> <tr> <td>资源套餐一</td> <td><input name="buy" type="radio" value="10" checked>10元</input> </td> </tr> <tr> <td>资源套餐二</td> <td><input name="buy" type="radio" value="20">20元 </td> </tr> <tr> <td>资源套餐三</td> <td><input name="buy" type="radio" value="30">30元 </td> </tr> <tr> <td>月租</td> <td><select name="rent" size="1"> <option value="50">50元/月</option> <option value="100">100元/月</option> </select> </td> </tr> <tr align="center"> <td colspan="2"><input type="submit" name="Submit" value="续订"> </td> </tr> </table> </form> </body> </html>注:此页面是内嵌在阿里平台的页面中的。图中虚线以下就是我们自己做的订购页面,虚线以上是平台提供的页面。如图:
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=(String)request.getSession().getAttribute("postdata");// 订购页面要原样回传的参数 returnUrl=(String)request.getSession().getAttribute("returnUrl"); //订购页面参数回传地址 String gmtStart=(String)request.getSession().getAttribute("gmtStart"); //订单开始时间 String gmtEnd=addMon(gmtStart,1);//计算订单结束时间,即订单开始时间加上订购时间,此处写死为一个月,但可在订购页面中让用户自行选择订购时间 /* * 订购类型不同时,传给平台的参数也是不同的。所以,根据订购类型,分别进行参数的组织 */ Map<String, Object> map=new HashMap<String, Object>(); 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<String, ?> entry : map.entrySet()) { if (notFirst) { buffer.append("&"); } else { notFirst = true; } Object value = 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()); } }跳转回平台后,出现如下页面:
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<String, Object> map = new HashMap<String, Object>(); 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; } }至此,我们的收费页面开发就完成了。
推荐阅读