1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
9 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
10 Cu.import("resource://gre/modules/Services.jsm");
12 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
13 "@mozilla.org/childprocessmessagemanager;1",
16 XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
17 "@mozilla.org/uuid-generator;1",
21 const PREF_DEBUG = "dom.payment.debug";
25 _debug = Services.prefs.getPrefType(PREF_DEBUG) == Ci.nsIPrefBranch.PREF_BOOL
26 && Services.prefs.getBoolPref(PREF_DEBUG);
35 dump("== Payment Provider == " + s + "\n");
39 dump("== Payment Provider ERROR == " + s + "\n");
42 const kPaymentFlowCancelled = "payment-flow-cancelled";
44 function PaymentProvider() {
47 PaymentProvider.prototype = {
48 init: function(aWindow) {
49 let docshell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
50 .getInterface(Ci.nsIWebNavigation)
51 .QueryInterface(Ci.nsIDocShell);
52 this._requestId = docshell.paymentRequestId;
53 this._oncancelObserver = this.oncancel.bind(this);
54 Services.obs.addObserver(this._oncancelObserver,
55 kPaymentFlowCancelled,
57 this._strategy = Cc["@mozilla.org/payment/provider-strategy;1"]
58 .createInstance(Ci.nsIPaymentProviderStrategy);
59 this._window = aWindow;
62 paymentSuccess: function(aResult) {
63 _debug && DEBUG("paymentSuccess " + aResult);
64 let glue = Cc["@mozilla.org/payment/ui-glue;1"]
65 .createInstance(Ci.nsIPaymentUIGlue);
66 glue.closePaymentFlow(this._requestId).then(() => {
67 if (!this._requestId) {
70 cpmm.sendAsyncMessage("Payment:Success", { result: aResult,
71 requestId: this._requestId });
75 paymentFailed: function(aError) {
76 _debug && DEBUG("paymentFailed " + aError);
77 let glue = Cc["@mozilla.org/payment/ui-glue;1"]
78 .createInstance(Ci.nsIPaymentUIGlue);
79 glue.closePaymentFlow(this._requestId).then(() => {
80 if (!this._requestId) {
83 cpmm.sendAsyncMessage("Payment:Failed", { errorMsg: aError,
84 requestId: this._requestId });
89 get paymentServiceId() {
90 return this._strategy.paymentServiceId;
93 set paymentServiceId(aServiceId) {
94 this._strategy.paymentServiceId = aServiceId;
98 * We expose to the payment provider the information of all the SIMs
99 * available in the device. iccInfo is an object of this form:
105 * dataPrimary: <boolean>
107 * "serviceIdN": {...}
111 return this._strategy.iccInfo;
114 oncancel: function() {
115 _debug && DEBUG("Cleaning up!");
117 this._strategy.cleanup();
118 Services.obs.removeObserver(this._oncancelObserver, kPaymentFlowCancelled);
124 classID: Components.ID("{82144756-72ab-45b7-8621-f3dad431dd2f}"),
126 contractID: "@mozilla.org/payment/provider;1",
128 QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
130 Ci.nsIDOMGlobalPropertyInitializer])
133 #if defined(MOZ_B2G_RIL) || defined(MOZ_WIDGET_ANDROID)
135 XPCOMUtils.defineLazyServiceGetter(this, "smsService",
136 "@mozilla.org/sms/smsservice;1",
139 const kSilentSmsReceivedTopic = "silent-sms-received";
141 // In order to send messages through nsISmsService, we need to implement
142 // nsIMobileMessageCallback, as the WebSMS API implementation is not usable
144 function SilentSmsRequest(aDOMRequest) {
145 this.request = aDOMRequest;
148 SilentSmsRequest.prototype = {
150 QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileMessageCallback]),
152 classID: Components.ID("{1d58889c-5660-4cca-a8fd-97ef63e5d3b2}"),
154 notifyMessageSent: function notifyMessageSent(aMessage) {
155 _debug && DEBUG("Silent message successfully sent");
156 Services.DOMRequest.fireSuccessAsync(this.request, aMessage);
159 notifySendMessageFailed: function notifySendMessageFailed(aError) {
160 DEBUG_E("Error sending silent message " + aError);
161 Services.DOMRequest.fireErrorAsync(this.request, aError);
165 PaymentProvider.prototype._silentNumbers = null;
167 PaymentProvider.prototype._silentSmsObservers = null;
169 PaymentProvider.prototype.sendSilentSms = function(aNumber, aMessage) {
170 _debug && DEBUG("Sending silent message " + aNumber + " - " + aMessage);
172 let request = Services.DOMRequest.createRequest(this._window);
174 if (this._strategy.paymentServiceId === null) {
175 DEBUG_E("No payment service ID set. Cannot send silent SMS");
176 Services.DOMRequest.fireErrorAsync(request,
177 "NO_PAYMENT_SERVICE_ID");
181 let smsRequest = new SilentSmsRequest(request);
182 smsService.send(this._strategy.paymentServiceId, aNumber, aMessage, true,
187 PaymentProvider.prototype.observeSilentSms = function(aNumber, aCallback) {
188 _debug && DEBUG("observeSilentSms " + aNumber);
190 if (!this._silentSmsObservers) {
191 this._silentSmsObservers = {};
192 this._silentNumbers = [];
193 this._onSilentSmsObserver = this._onSilentSms.bind(this);
194 Services.obs.addObserver(this._onSilentSmsObserver,
195 kSilentSmsReceivedTopic,
199 if (!this._silentSmsObservers[aNumber]) {
200 this._silentSmsObservers[aNumber] = [];
201 this._silentNumbers.push(aNumber);
202 smsService.addSilentNumber(aNumber);
205 if (this._silentSmsObservers[aNumber].indexOf(aCallback) == -1) {
206 this._silentSmsObservers[aNumber].push(aCallback);
211 PaymentProvider.prototype.removeSilentSmsObserver = function(aNumber, aCallback) {
212 _debug && DEBUG("removeSilentSmsObserver " + aNumber);
214 if (!this._silentSmsObservers || !this._silentSmsObservers[aNumber]) {
215 _debug && DEBUG("No observers for " + aNumber);
219 let index = this._silentSmsObservers[aNumber].indexOf(aCallback);
221 this._silentSmsObservers[aNumber].splice(index, 1);
222 if (this._silentSmsObservers[aNumber].length == 0) {
223 this._silentSmsObservers[aNumber] = null;
224 this._silentNumbers.splice(this._silentNumbers.indexOf(aNumber), 1);
225 smsService.removeSilentNumber(aNumber);
228 DEBUG("No callback found for " + aNumber);
233 PaymentProvider.prototype._onSilentSms = function(aSubject, aTopic, aData) {
234 _debug && DEBUG("Got silent message! " + aSubject.sender + " - " + aSubject.body);
236 let number = aSubject.sender;
237 if (!number || this._silentNumbers.indexOf(number) == -1) {
238 _debug && DEBUG("No observers for " + number);
242 // If the service ID is null it means that the payment provider asked the
243 // user for her MSISDN, so we are in a MT only SMS auth flow. In this case
244 // we manually set the service ID to the one corresponding with the SIM
245 // that received the SMS.
246 if (this._strategy.paymentServiceId === null) {
248 while(i < gRil.numRadioInterfaces) {
249 if (this.iccInfo[i].iccId === aSubject.iccId) {
250 this._strategy.paymentServiceId = i;
257 this._silentSmsObservers[number].forEach(function(callback) {
262 PaymentProvider.prototype._cleanup = function() {
263 if (!this._silentNumbers) {
267 while (this._silentNumbers.length) {
268 let number = this._silentNumbers.pop();
269 smsService.removeSilentNumber(number);
271 this._silentNumbers = null;
272 this._silentSmsObservers = null;
273 Services.obs.removeObserver(this._onSilentSmsObserver,
274 kSilentSmsReceivedTopic);
279 PaymentProvider.prototype.sendSilentSms = function(aNumber, aMessage) {
280 throw Cr.NS_ERROR_NOT_IMPLEMENTED;
283 PaymentProvider.prototype.observeSilentSms = function(aNumber, aCallback) {
284 throw Cr.NS_ERROR_NOT_IMPLEMENTED;
287 PaymentProvider.prototype.removeSilentSmsObserver = function(aNumber, aCallback) {
288 throw Cr.NS_ERROR_NOT_IMPLEMENTED;
293 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PaymentProvider]);