`
han2000lei
  • 浏览: 273176 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

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

阅读更多
在开发阿里软件的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

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; 
	} 
} 
至此,我们的收费页面开发就完成了。  




3
0
分享到:
评论
1 楼 liuxuejin 2015-03-02  
你好,我想请教你一个问题,我们打算开发一个BS架构的卖家服务应用,有个问题:应用的页面是怎么嵌入淘宝的后台的。我看其他的应用都是订购了,直接都能在淘宝后台操作应用的节面的,这是怎么做到的? 期待你的指导。

相关推荐

Global site tag (gtag.js) - Google Analytics