| <!-- |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| --> |
| <!DOCTYPE html> |
| <html> |
| <head> |
| <title>Sample: Payment Records Processor</title> |
| |
| <style> |
| body, td, div, span, p { |
| font-family:arial,sans-serif; |
| } |
| body { |
| padding:0px; |
| margin:0px; |
| } |
| .payment-processor-shadow { |
| filter: alpha(opacity=30); |
| -moz-opacity:.3; |
| opacity:0.3; |
| background-color:#000; |
| width:690px; |
| height:390px; |
| margin:5px 0px 0px 5px; |
| position:absolute; |
| z-index:100; |
| } |
| .payment-processor-border1 { |
| background-color:#E5ECF9; |
| width:690px; |
| height:390px; |
| position:absolute; |
| z-index:200; |
| } |
| .payment-processor-border2 { |
| background-color:#FFF; |
| margin:5px; |
| height:380px; |
| } |
| .payment-processor-content { |
| padding:20px; |
| font-size:13px; |
| } |
| .payment-processor-content #loading-tab { |
| color:#777; |
| } |
| .caption { |
| font-weight:bold; |
| width:80px; |
| display:inline; |
| } |
| .desc { |
| color:#007F00; |
| } |
| .head { |
| font-weight:bold; |
| } |
| </style> |
| |
| |
| <script type="text/javascript"> |
| |
| /** |
| * @static |
| * @class A sample records processor panel provides the UI and logic for the real records fetching |
| * and fixing requests to container api server. |
| * |
| * NOTE: |
| * |
| * All functions or logics or names in this page are customizable. Indeed containers have |
| * to customize them to make the UI consistent. This sample panel page is embeded in the |
| * parent container page as an iframe for better code structure, but indeed it is not |
| * necessary. It can be on the same page as container page. |
| * |
| * You can implement their processor panel page using this file but replace the UI and |
| * mock codes, or use your completely own codes. If you use your own page, just to make |
| * sure <code>shindig.paymentprocessor.initPaymentRecords</code> function is called with |
| * necessary callbacks (open and close event handlers) passed in when initializing the |
| * page. |
| * |
| */ |
| var myRecordsProcessorPanel = (function() { |
| |
| /** Element which holding this processor panel page in parent window. */ |
| var parentDiv; |
| |
| /** Just a reference to <code>shindig.paymentprocessor</code> object, which holding necessary |
| parameters needed in the payment process */ |
| var processor; |
| |
| /** |
| * Called by <code>shindig.paymentprocessor</code> when the counter |
| * panel is closing. |
| */ |
| function closeEvent() { |
| // Set the div in the parent window to invisible. |
| parentDiv.style.display = 'none'; |
| }; |
| |
| |
| /** |
| * Draws the pay counter panel UI itself. |
| * (NOTE that this page is a iframe in its parent container window); |
| * Assigns the submit callback and cancel callback to the buttons. |
| * So from this panel, submit or cancel actions can be made. |
| * |
| * @param {Object} paymentJson The payment parameters. |
| * @param {Object} extraParams The extra parameters for the payment |
| * procedure, including handler url, app title and spec. |
| * @param {Function} submitCallback The submit callback in |
| * <code>shindig.paymentprocessor</code>. |
| * @param {Function} cancelCallback The cancel callback in |
| * <code>shindig.paymentprocessor</code>. |
| */ |
| function openEvent() { |
| // Set the div in the parent window to visible. |
| parentDiv.style.display = 'block'; |
| document.getElementById('payment-appname').innerHTML = processor.getParam('appTitle'); |
| document.getElementById('payment-appspec').innerHTML = processor.getParam('appSpec'); |
| |
| document.getElementById('payment-records-close').onclick = cancelHandler; |
| |
| // The requestData is going to post to server. |
| var requestData = { |
| 'appSpec' : processor.getParam('appSpec'), |
| 'appTitle' : processor.getParam('appTitle'), |
| 'st' : processor.getParam('stoken'), // or other security token if needed |
| |
| 'params' : processor.getParam('reqParams'), |
| 'sandbox': processor.getParam('reqParams.sandbox') |
| }; |
| |
| ////////////////////////////////////////////////////////////////////////////////////// |
| // Here the logic should be on container sever for fetching payment records. // |
| // See the proposal doc Rivision#4. // |
| ////////////////////////////////////////////////////////////////////////////////////// |
| var sendFetchPaymentRecordsRequest = function(ajaxCallback) { |
| // The Server will fetch data from it's own database then response. |
| // Here is just a fake call. You should replace these codes with actual ajax. |
| // Wait 1 second to simulate the network connection. |
| window.setTimeout(function() { |
| // Get the payment records in database by querying with appSpec. Here uses mock data. |
| var mockData = [ |
| { |
| 'orderId' : 'ORDER_ID_FROM_APP_' + Math.round(Math.random() * 10000), |
| 'items': [ |
| {'skuId':'1234', 'price':'10', 'count': 5, 'description':'this is fake.'}, |
| {'skuId':'2345', 'price':'11', 'count': 7, 'description':'this is fake2.'} |
| ], |
| 'amount': 127, |
| 'message': 'Fake message', |
| 'paymentType': 'payment', |
| 'orderedTime': new Date().getTime(), |
| 'submittedTime': new Date().getTime(), |
| 'executedTime': new Date().getTime(), |
| 'responseCode': 'OK', |
| 'responseMessage': 'Payment done.', |
| 'paymentComplete': true, |
| 'sandbox': !!requestData['sandbox'] |
| }, { |
| 'orderId' : 'ORDER_ID_FROM_APP_' + Math.round(Math.random() * 10000), |
| 'items': [ |
| {'skuId':'3456', 'price':'5', 'count': 30, 'description':'this is fake3.'}, |
| {'skuId':'6789', 'price':'100', 'count': 1, 'description':'this is fake4.'} |
| ], |
| 'amount': 250, |
| 'message': 'Fake message2', |
| 'paymentType': 'payment', |
| 'orderedTime': new Date().getTime(), |
| 'submittedTime': new Date().getTime(), |
| 'executedTime': new Date().getTime(), |
| 'responseCode': 'APP_LOGIC_ERROR', |
| 'responseMessage': 'Payment failed on app.', |
| 'paymentComplete': false, |
| 'sandbox': !!requestData['sandbox'] |
| }, { |
| 'orderId' : 'ORDER_ID_FROM_APP_' + Math.round(Math.random() * 10000), |
| 'items': [ |
| {'skuId':'abcd', 'price':'3', 'count': 3, 'description':'this is fake5.'}, |
| {'skuId':'efgh', 'price':'12', 'count': 4, 'description':'this is fake6.'} |
| ], |
| 'amount': 57, |
| 'message': 'Fake message3', |
| 'paymentType': 'payment', |
| 'orderedTime': new Date().getTime(), |
| 'submittedTime': new Date().getTime(), |
| 'executedTime': new Date().getTime(), |
| 'responseCode': 'PAYMENT_ERROR', |
| 'responseMessage': 'Payment failed on container.', |
| 'paymentComplete': false, |
| 'sandbox': !!requestData['sandbox'] |
| } |
| ]; |
| |
| var max = Number(requestData['params']['max']); |
| if (!max) { |
| max = 3 // If not set or incorrectly set, set default value. |
| } |
| |
| ajaxCallback(mockData.slice(0, max)); |
| }, 1000); |
| }; |
| //////////////////////////////////////////////////////////////////////////////////// |
| |
| |
| |
| |
| |
| // Send ajax request |
| document.getElementById('loading-tab').style.display = 'block'; |
| |
| sendFetchPaymentRecordsRequest(function(responseData) { |
| document.getElementById('loading-tab').style.display = 'none'; |
| |
| var records = processor.getParam('records.payments'); |
| |
| var incompleteIds = []; |
| |
| // Generate the payment records table UI |
| var html = '<table border=1><tbody><tr class="head">' + |
| '<td>Amount</td><td>Message</td><td>SubmittedTime</td>' + |
| '<td>ResponseCode</td><td>ExecutedTime</td>'+ |
| '</tr>'; |
| for (var i = 0; i < responseData.length; i++) { |
| var paymentJson = responseData[i]; |
| var orderId = paymentJson['orderId']; |
| html += '<tr><td>' + paymentJson['amount'] + '</td>' + |
| '<td>' + paymentJson['message'] + '</td>' + |
| '<td>' + new Date(paymentJson['submittedTime']).toLocaleString() + '</td>' + |
| '<td>' + paymentJson['responseCode'] + '</td>' + |
| '<td id=\'td_' + orderId + '\'>'; |
| if (!paymentJson['paymentComplete']) { |
| // Show a 'FixIt' button for non-complete payment with ID equals orderId. |
| html += '<button id=\'' + orderId + '\'>FixIt</button>'; |
| // Add the incompletes to records. |
| records[orderId] = paymentJson; |
| incompleteIds.push(orderId); |
| } else { |
| html += new Date(paymentJson['executedTime']).toLocaleString(); |
| } |
| html += '</td></tr>'; |
| } |
| html += '</tbody></table>'; |
| document.getElementById('payment-records').innerHTML = html; |
| |
| // Assign onclick handler's for incomplete payments. |
| for (var j = 0; j < incompleteIds.length; j++) { |
| document.getElementById(incompleteIds[j]).onclick = submitHandler; |
| } |
| }); |
| |
| }; |
| |
| /** |
| * Called by submit button clicked by the user. |
| * |
| * This function should send the payment fixing request to container virtual currency |
| * api with Ajax POST. |
| */ |
| function submitHandler() { |
| var orderId = this.id; |
| var requestData = { |
| 'appSpec' : processor.getParam('appSpec'), |
| 'appTitle' : processor.getParam('appTitle'), |
| 'st' : processor.getParam('stoken'), // or other security token |
| |
| 'orderId' : orderId, |
| 'sandbox': processor.getParam('reqParams.sandbox') |
| }; |
| |
| ////////////////////////////////////////////////////////////////////////////////////// |
| // Here the logic should be on container sever for updating an incomplete payment. // |
| // See the proposal doc Rivision#4. // |
| ////////////////////////////////////////////////////////////////////////////////////// |
| var sendFixPaymentRecordRequest = function(ajaxCallback) { |
| // The Server will communicate with App Backend Server then response. |
| // Here is just a fake call. |
| // Wait 1 second to simulate the network connection. |
| window.setTimeout(function() { |
| // let say it will always succeed. |
| var responseData = {}; |
| |
| // Simulate success response. |
| responseData['paymentComplete'] = true; |
| responseData['executedTime'] = new Date().getTime(); |
| responseData['responseCode'] = 'OK'; |
| if (!!requestData['sandbox']) { |
| responseData['responseMessage'] = 'Fake success response in sandbox!'; |
| } else { |
| responseData['responseMessage'] = 'Fake success response!'; |
| } |
| |
| ajaxCallback(responseData); |
| }, 1000); |
| }; |
| ///////////////////////////////////////////////////////////////////////////////////// |
| |
| |
| |
| |
| |
| // Send ajax request |
| document.getElementById('loading-tab').style.display = 'block'; |
| |
| sendFixPaymentRecordRequest(function(responseData) { |
| document.getElementById('loading-tab').style.display = 'none'; |
| |
| if (responseData['responseCode'] != 'OK') { |
| // something fail, display exception message and let user try again. |
| document.getElementById(orderId).innerHTML = 'Try again'; |
| return; |
| } |
| |
| // If fixing request succeeded, replay the button with payment executed time. |
| document.getElementById('td_' + orderId).innerHTML = |
| new Date(responseData['executedTime']).toLocaleString(); |
| |
| // Updates the payment json object in the records. |
| var paymentJson = processor.getParam('records.payments.' + orderId); |
| paymentJson['paymentComplete'] = true; |
| paymentJson['executedTime'] = responseData['executedTime']; |
| paymentJson['responseCode'] = responseData['responseCode']; |
| paymentJson['responseMessage'] = responseData['responseMessage']; |
| |
| }); |
| |
| }; |
| |
| /** |
| * Invoked when cancel button clicked by user. Closes the processor. |
| */ |
| function cancelHandler() { |
| // You can also show a message to say the order is canceled. |
| // Here just call the callback and return. |
| |
| processor.setParam('records.responseCode', 'OK'); |
| |
| processor.closePaymentRecords(); |
| }; |
| |
| return { |
| |
| /** |
| * Initializes the counter module. It can be called by this page's <code>body.onload()</code> |
| * function or in other initializing steps. |
| * Note the <code>shindig.paymentprocessor</code> object is passed from the parent window. |
| */ |
| init: function() { |
| // Store the parent node in which there is an iframe holding this page. |
| parentDiv = window.frameElement.parentNode; |
| |
| processor = parent.shindig.paymentprocessor; |
| |
| // Initialize the paymentprocessor module with four events. |
| // The container need to fully implement these event functions for |
| // UI/Backend interaction. |
| processor.initPaymentRecords(openEvent, closeEvent); |
| } |
| |
| }; |
| })(); |
| |
| </script> |
| </head> |
| <body onload="myRecordsProcessorPanel.init();"> |
| <!-- Customize the UI --> |
| <div class="payment-processor-shadow"></div> |
| <div class="payment-processor-border1"> |
| <div class="payment-processor-border2"> |
| <div class="payment-processor-content"> |
| <p class="desc"> |
| This panel is in an iframe from another page in the same container domain:<br> |
| <b><script>document.write(window.location.href);</script></b> |
| </p> |
| |
| <div class="caption">App Name: </div><span id="payment-appname"></span><br> |
| <div class="caption">App Spec: </div><span id="payment-appspec"></span><br> |
| |
| <p id="payment-records"></p> |
| |
| <div id="button-tab"> |
| <button id="payment-records-close">Close</button> |
| </div> |
| |
| <div id="loading-tab" style="display:none">Please wait...</div> |
| </div> |
| </div> |
| </div> |
| </body> |
| </html> |
| |