运用Flex和Adobe AIR集成Amazon复杂的支付服务

使用Flex和Adobe AIR集成Amazon复杂的支付服务

使用Flex和Adobe AIR集成Amazon复杂的支付服务
2010年02月05日
  使用Flex和Adobe AIR集成Amazon复杂的支付服务出自9RIA天地会宝典
  跳转到: 导航, 搜索
  
  
  :点击此处查看或参与讨论吧^_^
  目录[隐藏]
  1 简介 2 基本要求 3 从Flash Player开始一个Amazon FPS工作流 4 从Adobe AIR开始amazon FPS的工作流 5 接下来去哪里 6 关于作者 简介原作者:Miti Pricope
  这篇文章描述了是一项使用Flex应用集成Amazon复杂支付服务(Amazon FPS)的技术,运行于Flash Player和Adobe AIR环境。我将会讲到安全和UI的考虑以及架构方面的内容,然后将会向你演示如何实现这整个东西。你可以下载完整示例来看所有事物是如何在一起运作的。
  服务器端我采用了PHP,不过这里的技术可以使用任何服务器端语言代替。
  这篇文章关注在Amazon FPS,但是很多想法也适用于PayPal Express Checkout(PayPal快速支付)。要了解更多关于PayPal支付服务的集成,请阅读使用Flex和Adobe AIR集成PayPal快速支付。
  基本要求为了能够更深入的实践本文中的内容,你需要如下的软件和文件:
  Flash Builder 4 beta
  下载 学习更多
  你也需要一个支持PHP的wen服务器和一个Amazon开发者账户。
  示例文件:
  AmazonFlexFiles(ZIP, 1.6MB) AmazonAIRFiles(ZIP, 1.8MB)
  预备知识
  为了能够按照本教程逐步学习,你需要基本熟悉Flex和ActionScript3.0,还有PHP。有一些Amazon FPS API的经验更佳。
  从Flash Player开始一个Amazon FPS工作流Amazon FPS是设计用于集成标准的,基于请求的web应用程序。虽然这种基于请求的模式已经在当今的web应用中成功实践,使用独立页面模式的富互联网应用(RIAs)也被证明是一种更好的电子商务流程和使用案例的情形。将支付服务当前的情况集成到RIAs的最大挑战是,类似与Amazon FPS的支付服务是设计工作于一种请求-响应的模式,而RIAs是状态化的。在这篇文章中,我描述了一种方法,使用RIAs集成Amazon FPS来处理如下场景:
  John访问一个录播视频的网站MitiOnDemand.tv。他选择了观看 The Matrix。此时,当Neo被要求选择红色还是蓝色药丸时,影片暂停,并要求John支付1美元,这是MitiOnDemand.tv上版权保护内容的费用。John已经准备行动了,决定使用Amazon支付这笔费用。这笔交易之后John很高兴地观看The Matrix。
  安全问题的考虑
  电子商务如此成功的一个原因是由于互联网被证明是一个安全的媒介用于转移钱和进行支付。Amazon FPS使用很多安全机制来确保服务中的所有的支付过程尽可能的安全:
  使用HTTPS协议与Amazon FPS进行通讯,确保所有的通讯对于第三方的访问是被保护的。 基于访问密钥和安全密钥的验证机制确保每个卖家是唯一认证的。 一个托管在Amazon服务器的Co-Branded页面,确保用户输入他们的凭证和核准所有的数量仅仅在Amazon域下有效。 审视Amazon FPS的这些安全因素可以得出一个非常重要的结论,尽可能地保护Amazon的安全密钥。更特别的是,因为Flex是一项客户端技术(即使代码已经被编译成字节码),在Flex应用中硬编码的敏感信息是一个高度不安全的实例。这意味着,一些关于计算的认证不能再Flex中进行,在这种情况下,Amazon安全密钥也不能放到Flex应用程序中。
  架构方法和UI解决方案
  总结一下,架构解决方案需要遵从两个同时成立的条件:
  它必须包含一个状态化的Flex RIA前端,并且是根据独立页面应用样式。 为了确保安全,Flex应用绝对不能处理Amazon安全密钥。 更深一层,AmazonCo-Branded页面必须进行用户授权验证,允许他们准确的查到他们从哪里登录的。 为了达到这些要求,我制定了如下的方案:
  所有支付相关的过程必须在服务器端进行,以确保Amazon密钥的安全。 访问到Co-Branded页面是通过弹出页面的形式,这样Flex应用程序能够保持独立页面模式。 从一个买家的角度来使用Flex在Amazon FPS基本快速开始的流程包含如下几步: 1. John, 这位买家,聚顶了他想要进行支付。他点击了Flex应用中的 Pay Now 按钮(如图1)。
  
  
  
  图1. Pay Now 按钮开始了整个流程。
  注意:在这点之前,要求所有的请求都是通过HTTPS进行的。
  2. 一个新的弹出窗口打开了,显示AmazonCo-Branded用户页面(CBUI)。这是一个标准的基于HTML的web应用,它托管在Amazon的服务器上。John使用他的e-mail ID和密码登入了他的Amazon支付账户(如图2)。
  
  
  
  图2. 买家在浏览器中登录Amazon支付账户
  3. 登入账户后,John查看了支付方式页面。这个页面允许他选择一个私人支付渠道作为交易方式,例如他的信用卡。John选择了他的Amazon支结余账户(ABT)作为支付方法,并点击了继续按钮(如图3)。
  
  
  
  图3. 使用选择一个支付方式进行交易
  4. 点击继续按钮之后,John将看到支付摘要页面。他可以核对支付具体信息然后点击确定(如图4)。
  
  
  
  图4. 这个支付摘要页面允许买家核对交易信息
  5. 接着John就被引导到了MitiOnDemand.tv的web站点。他重定向到的web页面(是在Co-Branded服务请求的returnURL参数中标明的)包含一个按钮,标签是"Return to Movie"。这个URL包含了一些额外的信息,包括授权状态,一个TokenId的引用,token标识是存储在Amazon的服务器上的。这个token标识是用于Amazon FPS交易过程中(例如支付)来实际初始化资金交易的。
  注意:支付交易并不是由Amazon FPS初始化的。MitiOnDemand.tv公司必须制造一个带有Co-Branded服务返回的TokenId的支付web服务请求。
  6. 当John点击Return to Movie,弹出窗口关闭,Flex应用程序接受到授权过程已经结束。现在,它可以调用服务器逻辑去实现真正的支付,并返回到硬盘,这样John最终能够进入The Matrix。
  从Flex中调用AmazonCo-Branded用户接口
  因为Amazon密钥需要存储在服务器上,它在服务器端也扮演者非常重要的角色。在这篇文章中,我选择了PHP做为服务器端语言,并且使用了Amazon FPS PHP SDK。你可以使用其他服务器语言,在这里标注高亮的原则和技巧同样适用。
  要调用Amazon CBUI,首先从Flex中打开弹出窗口。
  amazonFlex.mxml
  //Open the Pop-Up Window first. Using the//ExternalInterface call we can control the window appereanceExternalInterface.call('window.open','about:blank','amazonWindow','height=500,width=900,toolbar=no,scrollbars=yes');
  接下来的代码发送了一个包含用户选择的请求到服务器页面startPaymentFlex.php,在新打开的窗口中。
  amazonFlex.mxml
  var url:URLRequest = new URLRequest("https://miti.pricope.com/testAmazon/startPaymentFlex.php");url.data = new URLVariables();var obj:URLVariables = new URLVariables();url.data.movieId = moviePick.selectedItem.value;url.data.paymentReason = 'Enter The Matrix';url.method = "GET";navigateToURL(url, "amazonWindow");
  使用navigateToURL()来确保如果window.open不能正常工作(例如因为严格的弹出阻止),用户可以继续工作流。
  在服务器页面上,我产生一个Amazon CBUI请求并重定向浏览器到那个请求:
  startPaymentFlex.php
  session_start();function getMovieAmount($movieId) {//you can replace this function with a more sophisticated onereturn 1;}$obj = new Amazon_FPS_CBUIUtils(AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY);$obj->setMandatoryParams("SingleUse", "https://" . $_SERVER['HTTP_HOST'] . "/testAmazon/return.php");//The refernce ID is unique to your business//You can replace the standard UID php function with more suitable function$ref = uniqid("amazon");$obj->setCallerReference($ref);$obj->setTransactionAmount(getMovieAmount($_GET['movieId']));$obj->setPaymentReason($_GET['paymentReason']);$qs = $obj->getURL() ;//We use session data to store the state of the application between requests//The amount will be used later on (in return.php) to invoke the FPS Pay method//We also hold the status of the transaction. This will be requested//by the Flex App$_SESSION['status'] = 'START';$_SESSION['transaction_amount'] = getMovieAmount($_GET['movieId']);$_SESSION['movieId'] = $_GET['movieId'];header("Location:$qs")
  注意这段代码仍然存储了一些数据在session变量中,包括状态、交易数量以及影片ID。这些值在返回页面后正式访问Amazon FPS支付方法时会用到。
  在一个真实的电子商务网站中,我强烈建议你同时将应用程序状态中数据库中存储日志,这将确保之后所有交易步骤的安全性。
  从Amazon返回,进行支付
  用户完成了Amazon CBUI工作流和交易授权之后,他会被重定向到一个指定了returnURL参数的回调Co-Branded服务请求页面。在这个案例中,他被重定向到return.php,这个页面证明了回调的请求是有效的,然后调用Amazon FPS的支付方法来进行资金交易。
  为了这一部,我再次使用了Amazon FPS PHP SDK。
  return.php
  function validateQueryString(){    echo "validing the query string now\n";    $querystring = $_SERVER['QUERY_STRING'];    echo $_GET['signature'];    $obj = new Amazon_FPS_CBUIUtils(AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY);    //Original signature received in response from Amazon FPS should be specified.    $signatureMatched = $obj->validateQueryString($querystring,$_GET['signature']);     if ($signatureMatched) {        echo "signature matched \n";        $request =  new Amazon_FPS_Model_PayRequest();        //set the proper senderToken here.        $request->setSenderTokenId($_GET['tokenID']);        $amount = new Amazon_FPS_Model_Amount();        $amount->setCurrencyCode("USD");        //set the transaction amount here;        $amount->setValue($_SESSION['transaction_amount']);        $request->setTransactionAmount($amount);        //set the unique caller reference here.        $request->setCallerReference($_GET['callerReference']);        $service = new Amazon_FPS_Client(AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY);        invokePay($service, $request);    }    else        echo "Signature did not match \n";}validateQueryString();
  我使用了invokePay方法,对方法做了一点点小的修改,这个方法可以在Amazon FPS PHP SDK中找到。当交易成功后,这个代码就将状态存储在session变量中。
  return.php
  function invokePay(Amazon_FPS_Interface $service, $request){     try {             $response = $service->pay($request);              echo ("Service Response\n");             echo ("===============================================\n");             echo("        PayResponse\n");             if ($response->isSetPayResult()) {                 echo("            PayResult\n");                 $payResult = $response->getPayResult();                 if ($payResult->isSetTransactionId())                 {                     echo("TransactionId\n");                     echo("" . $payResult->getTransactionId() . "\n");                 }                 if ($payResult->isSetTransactionStatus())                 {                     echo("TransactionStatus\n");                     echo("" . $payResult->getTransactionStatus() . "\n");                     //CHECK Transaction Status is Success                     $_SESSION['status'] = 'OK';                 }             }             if ($response->isSetResponseMetadata()) {                 echo("            ResponseMetadata\n");                 $responseMetadata = $response->getResponseMetadata();                 if ($responseMetadata->isSetRequestId())                 {                     echo("RequestId\n");                     echo("" . $responseMetadata->getRequestId() . "\n");                 }             }     } catch (Amazon_FPS_Exception $ex) {        echo("Caught Exception: " . $ex->getMessage() . "\n");        echo("Response Status Code: " . $ex->getStatusCode() . "\n");        echo("Error Code: " . $ex->getErrorCode() . "\n");        echo("Error Type: " . $ex->getErrorType() . "\n");        echo("Request ID: " . $ex->getRequestId() . "\n");        echo("XML: " . $ex->getXML() . "\n");    }}
  使用ExternalInterface来通知Flex应用程序
  剩下的最后一件事情就是通知Flex应用程序并且关闭弹出窗口。
  首先,我需要准备Flex应用程序。我创建了一个支付通知函数来被弹出窗口通过ExternalInterface API调用。因为JavaScript的调用可以被重写(通过Firebug实例),ExternalInterface调用是不安全的。所以我仅仅调用通知Flex应用程序,Amazon工作流已经结束。然后Flex应用程序通过HTTPS从服务器恢复状态。
  amazonFlex.mxml
  private function paymentNotification():void { var srv:HTTPService = new HTTPService(); srv.url = "https://miti.pricope.com/testAmazon/paymentStatus.php"; srv.addEventListener(ResultEvent.RESULT,function (event:ResultEvent):void { Alert.show("Status: " + event.result.status); }); srv.send();}
  在这个案例中paymentStatus.php是一个简单的服务器端脚本,通过一个简单的XML格式返回状态。
  paymentStatus.php
  session_start();echo '' . $_SESSION['status'] . ''
  paymentNotification()函数必须显示通过ExternalInterface暴露提供给JavaScript调用。添加回调最佳的时机是当Flex应用程序结束初始化时,applicationComplete事件处理是一个很合适的地方。
  amazonFlex.mxml
  //This will be used by return.php to notify Flex App that the payment has been madeExternalInterface.addCallback('paymentNotification',paymentNotification);
  从return.php返回,剩下的事情就是关闭弹出窗口并通知Flex应用程序。
  return.php
   function gotoflex() { window.opener.window.document.getElementById('testAmazon').paymentNotification(); window.close(); }Flex APP" />
  安装实例文件 按照如下步骤来安装示例文件: 1. 解压完成的文件到一个临时位置,例如/tmp。 2. 将amazonFlex文件夹拷贝到你的web根目录,例如/work/www。 3. 编辑amazonFlex/amazon-fps/src/Amazon/FPS/Samples/.config.inc.php,找到如下行:
  define('AWS_ACCESS_KEY_ID', 'YOUR KEY');define('AWS_SECRET_ACCESS_KEY', 'YOUR SECRET KEY');
  用你自己的Amazon访问密钥和安全密钥替换值。 4. 在Flash Builder 4 beta中选择 "文件 > 导入",然后选择Flash Builder项目。 5. 选择示例文件amazonFlex.fxp。 6. 为你的安装明确输出目录的位置,Web root,Root URL合适的值。我使用的设置如图5所示,你的设置也许不同。
  
  
  
  图5. 当导入项目时明确路径变量
  从Adobe AIR开始amazon FPS的工作流注意:因为在这篇文章中描述的Flash Player和Adobe AIR实现都是采用Flex,下面的一些讨论将重复之前介绍过的一些概念。
  尽管AIR应用程序运行在桌面,并且本地安全的限制与浏览器中不同,从支付网关的角度来看事情并没有改变。因为AIR是一种客户端的技术,AIR应用程序中的硬编码敏感信息是一种非常不安全的时间(即使编码已经编译成字节码)。这意味着一些重要的相关计算不能编辑在AIR应用程序中,Amazon安全密码也不能放在AIR应用程序中。
  架构方法和UI解决方案
  与Flex应用程序类似,架构方面也有几个前提要求:
  它必须包含一个状态化的AIR前端,并且是根据独立页面应用样式。 为了确保安全,AIR应用绝对不能处理Amazon安全密钥。 更深一层,AmazonCo-Branded页面必须进行用户授权验证,允许他们准确的查到他们从哪里登录的。 为了达到这些要求,我制定了如下方案:
  所有支付相关的过程必须在服务器端进行,以确保Amazon密钥的安全。当然,这意味着当支付行为发生时,AIR应用必须是联网的。 访问的Co-Branded页面必须是一个浏览器页面。 注意:尽管从技术角度将使用AIR运行时的HTML容器来访问Co-Branded页面也是可行的,但这是一个不安全的实践,因为最终用户不能真实地确认他是进入了Amazon网站的信用卡页面。在浏览器中,提供了有效的反视角方法,他可以检查URL和安全认证。
  从AIR中调用Amazon Co-Branded用户界面
  以下是介绍如何从AIR应用程序中在新的浏览器窗口总打开一个Amazon CBUI。
  amazonAir.mxml
  var url:URLRequest = new URLRequest("http://localhost/amazonAIR/startPayment.php");url.data = new URLVariables();var obj:URLVariables = new URLVariables();url.data.movieId = 1;url.data.paymentReason = 'Enter The Matrix';url.method = "GET";navigateToURL(url, "new");
  这步剩下的事情和工作流下一步的事情与使用Flex运行于Flash Player中调用Amazon CBUI类似。查看从Flex中调用AmazonCo-Branded用户接口和从Amazon返回,进行支付章节获取更多信息。
  使用LocalConnection来通知AIR应用
  剩下的最后一件事就是通知AIR应用,并把它置于最前显示。
  这要求浏览器应用于AIR应用进行通讯。为了完成这个任务,使用LocalConnection机制。LocalConnection对象能够在运行于相同客户端电脑中的各文件间通讯,有可能是运行与不同的应用中 -- 例如,运行在浏览器中的SWF内容和运行于Adobe AIR中的SWF内容。
  因为LocalConnection可以使用例如DNS复写等技术欺骗,所以它不能用于传输敏感性系。另外,由于AIR应用是独立于浏览器的,他们不能共享服务器的session。一个简单的通知是不够的;我希望能够传递session id。这个不是敏感信息,但是它允许AIR应用从服务器获取到浏览器设置的敏感信息。
  简单一点,returnAir.php页面已经有了一个小的Flex应用可以用于与AIR应用通讯。Flex应用获取到cookies信息,然后通过LocalConnection将它发送给AIR应用程序。
  amazonAIRReturn.mxml
  private var outbound:LocalConnection = new LocalConnection();private function gotoAIR():void {  //get the cookie string  ExternalInterface.call('eval','window.cookieStr = function () {return document.cookie};') var cookieStr:String = ExternalInterface.call('cookieStr');   outbound.connect("paymentSample");  outbound.send("app#amazonAIR:paymentSample","notifyPayment",cookieStr); //outbound.send("app#testAmazonAir.F0B3F68E1857B8A07069FED1D0638CAF200F76EB.1:paymentSample","notifyPayment",cookieStr); outbound.close();}
  当从Flash Builder 4 beta中运行时,AIR应用程序还没有Publisher ID,所以连接名称是app#amazonAIR:paymentSample。当打成安装包之后,AIR应用将获取到它自身的Publisher ID,所以连接名称将会是下面这样:
  app#amazonAIR.F0B3F68E1857B8A07069FED1D0638CAF200F76EB.1:paymentSample.
  你可以决定安装AIR应用程序的publisher ID,查看程序安装目录的META-INF/AIR/publisherid。
  回到AIR应用程序中,我公布了一个函数可以通过LocalConnection调用的。
  amazonAIR.mxml
  //This will be used by return.php to notify the AIR App//that the payment has been madeprivate var inbound:LocalConnection = new LocalConnection(); private function initApp():void { //only allow connections from localhost //you need to replace "localhost" with the final domain //where your application will be hosted inbound.allowDomain("localhost"); inbound.client = new Object(); //this is the function that will be called by the Browser App inbound.client.notifyPayment = paymentNotification; inbound.connect("paymentSample");}
  上面的代码中,paymentNotification()是一个函数,接收cookie字符串作为参数,查询服务器以检查交易状态。
  amazonAIR.mxml
  public function paymentNotification(cookieStr:String):void { var srv:HTTPService = new HTTPService(); srv.headers.Cookie = cookieStr; srv.url = "http://localhost/amazonAIR/paymentStatus.php"; srv.addEventListener(ResultEvent.RESULT,function (event:ResultEvent):void { nativeApplication.activate(); if (event.result.status == 'OK') { currentState = 'Succes'; } else { currentState = 'Fail'; } }); srv.send();}
  安装实例文件
  按照如下步骤安装Adobe AIR实现的实例文件:
  1. 解压完成的文件到临时位置,如/tmp。 2. 在Flash Builder 4 beta中选择"文件 > 导入",选择 Flash Builder项目。 3. 选择AmazonAIR.fxp示例文件。 4. 将amazonAIR拷贝到你的Web Root目录,例如/work/www。 5. 编辑amazonAIR/amazon-fps/src/Amazon/FPS/Samples/.config.inc.php,找到如下行:
  define('AWS_ACCESS_KEY_ID', 'YOUR KEY');define('AWS_SECRET_ACCESS_KEY', 'YOUR SECRET KEY');
  使用你自己的Amazon访问密钥和安全访问密钥替换值。 6. 在Flash Builder 4 beta中再次选择"文件 > 导入",选择Flash Builder项目。 7. 选择amazonAIRReturn.fxp。 8. 为你的安装明确输出目录的位置(在你的web根目录下已经有了amazonAIR),Web root,Root URL合适的值。我使用的设置如图6所示,你的设置也许不同。
  
  
  
  图6. 为你的系统设置路径变量
  接下来去哪里在本文中,我已经介绍了一种使用Amazon Flexible Payment System和Flex来安全的实现支付工作流的方法,可以在Flash Player和Adobe AIR中运行。
  下面的资源提供了在本文中使用到的更详细的技术说明:
  Amazon FPS开发者资源
  ExternalInterface类的说明文档
  LocalConnection类的说明文档
  本文基于Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License协议发布。
  关于作者Mihai (Miti) Pricope是Adobe的一名平台传教士。他的旅程是从7年前他的宿舍中开始的,当时他和2名同事一起在一个叫InterAKT的小公司里。他提供了第一个支持在 Dreamweaver Ultradev (PHAkt)中支持PHP的插件,还有很多其他的Dreamweaver扩展来帮助将InterAKT植入Macromedia/Adobe中。Miti当他看见第一个beta版时就开始沉迷于Flex,并与2006年将InterAKT卖给了Adobe,他自己也加入了Flex Builder团队作为一名优秀的传教士的角色。
  取自"http://wiki.9ria.com/index.php/%E4%BD%BF%E7%94%A8Flex%E5%92%8CAdobe_AIR%E9%9B%86%E6%88%90Amazon%E5%A4%8D%E6%9D%82%E7%9A%84%E6%94%AF%E4%BB%98%E6%9C%8D%E5%8A%A1"