1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 import oauth2 as oauth
30 import httplib2, pickle, os, types, time, urllib, simplejson, uuid
31 from types import *
32 from bluevia_helpers import _parseAdResponse
33 from bluevia_helpers import _encodeMultipart
34 from bluevia_helpers import _decodeMultipart
35
36
37
38
39
41 """
42 The BlueVia base class. All other BlueVia classes are inherited from this class BlueVia.
43
44 Mainly Stores consumer and access_token, provides the generic _signAndSend(...) a debug() method
45
46 HOWTO USE
47 =========
48
49 oAuth routines
50 --------------
51
52 >>> o = bluevia.BlueViaOauth('<secret>', '<key>')
53 >>> o.fetch_request_token()
54
55 Returns the oAuth URL for user authorization
56
57 Successful authorization returns an oAuth verifier
58
59 >>> o.fetch_access_token("<verifier>")
60 >>> o.saveAccessToken("newtok.pkl")
61
62
63 SMS outbound routines
64 ---------------------
65
66 >>> s = bluevia.BlueViaOutboundSms()
67 >>> s.loadAccessToken("newtok.pkl")
68 >>> s.sendSMS([myMobileNumber], "Hallo Welt")
69
70 Returns the delivery URL
71
72 >>> s.deliveryStatus("<deliveryURL>")
73
74 SMS receive routines
75 --------------------
76
77 For Sandbox testing use an app that can both send SMS and receive SMS with keyword "BlueViaRocks"
78
79 1) Send a fake SMS
80
81 >>> s = bluevia.BlueViaOutboundSms()
82 >>> s.loadAccessToken("smsmo.pkl")
83 >>> s.sendSMS(["445480605"], "SANDBlueViaRocks so much!")
84
85 2) Receive SMS with App
86
87 >>> i = bluevia.BlueViaInboundSMS()
88 >>> i.loadAccessToken("smsmo.pkl")
89 >>> i.receiveSMS("445480605") # UK shortcode
90
91 For live testing use a mobile from the developer (e.g. the one owning the application)
92
93 1) Send "TESTBlueViaRocks so much live!" to 445480605
94
95 2) Retrieve from Test System
96
97 >>> i = bluevia.BlueViaInboundSMS("") # set sandbox parameter to "" makes test calls
98 >>> i.loadAccessToken("smsmo.pkl")
99 >>> i.receiveSMS("445480605")
100
101 Location routines
102 -----------------
103
104 >>> l = bluevia.BlueViaLocation()
105 >>> l.loadAccessToken("newtok.pkl")
106 >>> l.locateTerminal():
107
108 User context routines
109 ---------------------
110
111 >>> u = bluevia.BlueViaUserContext()
112 >>> u.loadAccessToken("newtok.pkl")
113 >>> u.getInfo():
114
115 Advertising routines
116 --------------------
117
118 >>> a = bluevia.BlueViaAds("<adspace Id>")
119 >>> a.loadAccessToken("newtok.pkl")
120 >>> a.getAd_3l(keywordList = ["sport"])
121
122 Payment routines
123 ----------------
124
125 >>> p = bluevia.BlueViaPayment('<secret>', '<key>')
126 >>> p.fetch_request_token(<amount>, <currency>, <serviceId>, <serviceName>)
127 >>> p.fetch_access_token(<verifier>)
128 >>> p.savePaymentInfo("payment.pkl") # optional, token valid for 48 h
129 >>> p.loadPaymentInfo("payment.pkl") # optional
130 >>> p.issuePayment()
131 >>> p.checkPayment(<transactionId>)
132 """
133
134 access_token = None
135 consumer = None
136 realm = None
137 environment = ""
138 version = None
139 debugFlag = False
140
141 http = httplib2.Http()
142
143
144 - def _signAndSend(self, requestUrl, method, token, parameters={}, body="", \
145 extraHeaders={}, is_form_encoded = False):
146 """
147 Generic method to call an oAuth authorized API in BlueVia including oAuth signature.
148
149 @param requestUrl: (string): The BlueVia URL
150 @param method: (string): HTTP method, "GET" or "POST"
151 @param token: (oauth.Token): Usually the Access Token. During oAuth Dance None or Request Token
152
153 @param parameters: (dict): Necessary call paramters, e.g. version, alt. Default: None
154 @param body: (string): Body of the HTTP call. Default: ""
155 @param extraHeaders: (dict): Some calls need extra headers, e.g. {"Content-Type":"application/json"}. Default: None
156 @param is_form_encoded: (boolean): If True parameters are send as form encoded HTTP body. DEFAULT: False
157
158 @return: (tuple): (HTTP response, HTTP response data)
159 """
160
161 req = oauth.Request.from_consumer_and_token(self.consumer, token, method, requestUrl, \
162 parameters, body, is_form_encoded)
163 req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), self.consumer, token)
164
165 headers = req.to_header(realm=self.realm)
166 if parameters.has_key("xoauth_apiName"):
167 headers['Authorization'] += ', xoauth_apiName="%s"' % parameters.get("xoauth_apiName")
168
169 if extraHeaders:
170 headers.update(extraHeaders)
171
172 if is_form_encoded:
173
174 params = [p for p in parameters.items() if p[0] in ["version","alt"]]
175 else:
176
177 params = [p for p in parameters.items() if p and p[0][:6] != "oauth_"]
178
179 query = None
180 if params:
181 query = "&".join(["%s=%s" % (p[0], p[1]) for p in params])
182 if query:
183 requestUrl += "?" + query
184
185 if self.debugFlag: self._debug(requestUrl, query, headers, body, token, req)
186
187 response, content = self.http.request(requestUrl, method, body, headers)
188
189 if self.debugFlag:
190 print('response["status"] = %s' % response["status"])
191 print('content =\n %s' % content)
192
193 return response, content
194
195
197 """
198 Load Consumer Credentials and Access Token from disk (pickle file).
199
200 @note:
201 Unencrypted storage. Use only for testing!
202
203 @param path: (string): Path to pickle file
204
205 @return: (boolean): True if successfully loaded
206 """
207
208 if os.path.exists(path):
209 fd = open(path, "r")
210 self.consumer, self.access_token = pickle.load(fd)
211 fd.close()
212 return True
213 else:
214 return False
215
216
218 """
219 Set the Consumer credentials.
220
221 @param consumer: (oauth.Token): The consumer credentials as provided by getCosumer in class BlueViaOauth
222 """
223
224 self.consumer = consumer
225
226
228 """
229 Set the Access Token.
230
231 @param access_token: (oauth.Token): The oAuth access token as provided by getAccessToken in class BlueViaOauth
232 """
233
234 self.access_token = access_token
235
236
238 """
239 Check availability of access token
240 """
241
242 return (self.consumer != None) and (self.access_token != None)
243
244
246 """
247 Set or unset the debug flag
248
249 @param dbgFlag: (boolean): If True debug information will be printed to stdout
250 """
251
252 self.debugFlag = dbgFlag
253
254
255 - def _debug(self, requestUrl, query, headers, body, token, req):
256 """
257 Prints aut anything relevant for oAuth debugging: URL, method, body, headers, signature base string, ...
258
259 @note: Internal method
260 """
261
262 print("\nurl = " + requestUrl)
263 if not query: query = ""
264 print("\nquery = " + query)
265 print("\nhead = " + simplejson.dumps(headers, indent = 2).replace(", ", ",\n"))
266 try:
267 bstr = simplejson.dumps(simplejson.loads(body), indent = 2)
268 except:
269 bstr = body
270 print("\nbody = " + bstr)
271 sm = oauth.SignatureMethod_HMAC_SHA1()
272 key, base = sm.signing_base(req, self.consumer, token)
273 print("\noAuth signature components")
274 print("\nbase = " + base)
275 print("\nkey = " + key)
276
277
278
279
280
281
283 """
284 This class provides the methods for the oAuth Dance.
285 It supports Out Of Band authorization as defined in oAuth 1.0a
286 """
287
288 - def __init__(self, consumer_key, consumer_secret, realm="BlueVia"):
289 """
290 Initialize the BlueViaOauth object
291
292 @param consumer_key: (string): Key of the Consumer Credentials
293 @param consumer_secret: (string): Secret of the Consumer Credentials
294
295 @param realm: (string): Realm string. Default: "BlueVia"
296 """
297
298 self.realm = realm
299 self.consumer = oauth.Consumer(consumer_key, consumer_secret)
300 self.request_token_url = 'https://api.bluevia.com/services/REST/Oauth/getRequestToken'
301 self.access_token_url = 'https://api.bluevia.com/services/REST/Oauth/getAccessToken'
302 self.authorization_url = 'https://connect.bluevia.com/authorise'
303 self.request_token = None
304
305
307 """
308 First call of the oAuth Dance. Provide the Consumer Credential and request the Request Token
309
310 @param callback: (string): The callback URL or "oob". Default: "oob"
311
312 @return: (tuple): (HTTP status, authorization URL). HTTP status == "200" for success
313 """
314
315
316 response, content = self._signAndSend(self.request_token_url, "POST", None, parameters={"oauth_callback":callback})
317 if response["status"] == '200':
318 self.request_token = oauth.Token.from_string(content)
319 return int(response["status"]), "%s?oauth_token=%s" % (self.authorization_url, self.request_token.key)
320 else:
321 return int(response["status"]), content
322
323
325 """
326 The final step of the oAuth Dance. Exchange the Request Token with the Access Token
327
328 @param verifier: (string): The oAuth verifier of the successful user authorization
329
330 @return: (string): HTTP status == "200" for success
331 """
332
333 assert type(verifier) is StringType and verifier!= "", "Oauth 'verifier' must be a non empty string"
334
335 self.request_token.set_verifier(verifier)
336 response, content = self._signAndSend(self.access_token_url, "POST", self.request_token, parameters={})
337 if response["status"] == '200':
338 self.access_token = oauth.Token.from_string(content)
339 return int(response["status"])
340 else:
341 return int(response["status"]), content
342
343
345 """
346 Save the Access Token.
347
348 Note: Unencrypted storage. Use only during development
349
350 @param path: (string): Path to file on disk (pickle file)
351 """
352
353 assert type(path) is StringType and path!= "", "'path' must be a non empty string"
354
355 fd = open(path, "w")
356 pickle.dump((self.consumer, self.access_token), fd)
357 fd.close()
358
359
361 """
362 Retrieve the Consumer Credentials
363 """
364
365 return self.consumer
366
367
369 """
370 Retrieve the Access Token
371 """
372
373 return self.access_token
374
375
376
377
378
379
381 """
382 The BlueVia class for sending and tracking SMS.
383 """
384
385 - def __init__(self, sandbox = "_Sandbox", realm = "BlueVia", version="v1"):
386 """
387 Initialize the BlueViaOutboundSms object
388
389 @param sandbox: (string): Indicates whether testing should be done in Sandbox mode. Use "" for real network access; Default: "_Sandbox"
390 @param realm: (string): Realm string; Default: "BlueVia"
391 @param version: (string): BlueVia API version; Default: "v1"
392 """
393
394 self.environment = sandbox
395 self.realm = realm
396 self.version = version
397 self.outbound_sms_url = "https://api.bluevia.com/services/REST/SMS%s/outbound/requests"
398
399
400 - def sendSMS(self, addresses, message):
401 """
402 Send SMS via BlueVia to one or more recipients
403
404 @param addresses: (array): An array of mobile numbers (string) in the form "44 (for UK) 7764735478" (Mobile number without first zero and no spaces)
405 @param message: (string): A maximum 160 char string containing the SMS message
406
407 @return: (tuple): (HTTP status, deliveryURL). HTTP status == "201" for success. Use deliverURL in method deliverStatus.
408 """
409
410 assert self.hasCredentials(), "load oAuth credentials first or execute oAuth Dance"
411 assert (type(addresses) is ListType and addresses!=[]) or \
412 (type(addresses) is StringType and addresses!="") , "'addresses' must be a non empty string or a non empty array of strings"
413 assert type(message) is StringType and message!= "", "'message' must be a non empty string"
414
415 if type(addresses) == types.StringType:
416 addresses = [addresses]
417 addrlist = ",".join(['{"phoneNumber":"%s"}' % a for a in addresses])
418 if len(addresses) > 1: addrlist = '[%s]' % addrlist
419
420 body = '{"smsText": {"address": %s,"message": "%s", "originAddress": {"alias": "%s"}}}' \
421 % (addrlist, message, self.access_token.key)
422
423 parameters = {"version":self.version, "alt":"json"}
424
425 response, content = self._signAndSend(self.outbound_sms_url % self.environment, "POST", self.access_token, \
426 parameters=parameters, body=body, \
427 extraHeaders={"Content-Type":"application/json;charset=UTF8"})
428 if response["status"] == '201':
429 return int(response["status"]), response["location"]
430 else:
431 return int(response["status"]), content
432
433
435 """
436 Track the delivery of a BlueVia SMS
437
438 @param deliveryURL: (string): deliveryURL provided by sendSMS method
439
440 @return: (tuple): (HTTP status, (dict) deliveryReceipt). HTTP status == "200" for success.
441 """
442
443 assert self.hasCredentials(), "load oAuth credentials first or execute oAuth Dance"
444 assert type(deliveryURL) is StringType and deliveryURL!= "", "'deliveryURL' must be a non empty string"
445
446 parameters = {"version":self.version, "alt":"json"}
447 response, content = self._signAndSend(deliveryURL, "GET", self.access_token, parameters=parameters)
448 if response["status"] == '200':
449 return int(response["status"]), simplejson.loads(content)["smsDeliveryStatus"]
450 else:
451 return int(response["status"]), content
452
453
454
455
456
457
459 """
460 The BlueVia class for receiving SMS.
461 """
462
463 - def __init__(self, sandbox = "_Sandbox", realm = "BlueVia", version="v1"):
464 """
465 Initialize the BlueViaInboundSMS object
466
467 @param sandbox: (string): Indicates whether testing should be done in Sandbox mode. Use "" for real network access; Default: "_Sandbox",
468 @param realm: (string): Realm string; Default: "BlueVia"
469 @param version: (string): BlueVia API version; Default: "v1"
470 """
471
472 self.environment = sandbox
473 self.realm = realm
474 self.version = version
475 self.inbound_sms_url = "https://api.bluevia.com/services/REST/SMS%s/inbound/%s/messages"
476 self.inbound_notification_url = "https://api.bluevia.com/services/REST/SMS%s/inbound/subscriptions"
477
479 """
480 Receive all SMS sent to the shortcode with the Keyword defined during BlueVia App generation
481
482 @param shortcode: (string): SMS shortcode including country code without "+", e.g. "44"
483
484 @return: (tuple): (HTTP status, (dict) receivedSMS). HTTP status == "200" for success.
485 """
486
487 assert self.hasCredentials(), "load oAuth credentials first or execute oAuth Dance"
488 assert type(shortcode) is StringType and shortcode!= "", "'shortcode' must be a non empty string"
489
490 url = self.inbound_sms_url % (self.environment, shortcode)
491 parameters = {"version":self.version, "alt":"json"}
492 response, content = self._signAndSend(url, "GET", self.access_token, parameters=parameters)
493 if response["status"] == '200':
494 return int(response["status"]), simplejson.loads(content)['receivedSMS']
495 else:
496 return int(response["status"]), content
497
498
500 """
501 Subscribe to Receive SMS notifications
502
503 @param shortcode: (string): SMS shortcode including country code without "+", e.g. "44"
504 @param keyword: (string): The registered SMS Keyword of the application
505 @param endpoint: (string): The url to which BlueVia shall post the relevant SMS
506 @param correlator: (string): The correlator allows to identify the subscription
507
508 @return: (tuple): (HTTP status, unsubscribeURL). HTTP status == "201" for success. Use unsubscribeURL in method unsubscribeNotifications.
509 """
510
511 assert self.hasCredentials(), "load oAuth credentials first or execute oAuth Dance"
512 assert type(shortcode) is StringType and shortcode!= "", "'shortcode' must be a non empty string"
513
514 url = self.inbound_notification_url % (self.environment)
515
516 body = '{"smsNotification": {\
517 "reference": { "correlator": "%s", "endpoint": "%s"}, \
518 "destinationAddress": {"phoneNumber": "%s"}, \
519 "criteria": "%s" }}}' % (correlator, endpoint, shortcode, keyword)
520
521 parameters = {"version":self.version, "alt":"json"}
522
523 response, content = self._signAndSend(url, "POST", self.access_token, \
524 parameters=parameters, body=body, \
525 extraHeaders={"Content-Type":"application/json;charset=UTF8"})
526 if response["status"] == '201':
527 return int(response["status"]), response["location"]
528 else:
529 return int(response["status"]), content
530
532 """
533 Unsubscribe to Receive SMS notifications
534
535 @param url: (string): The unsubscribe URL returned by the subscribeNotifications method
536
537 @return: (tuple): (HTTP status, unsubscribeURL). HTTP status == "204" for success.
538 """
539
540 parameters = {"version":self.version}
541 response, content = self._signAndSend(url, "DELETE", self.access_token, parameters=parameters)
542
543 return int(response["status"]), content
544
545
546
547
548
550 """
551 The BlueVia class for sending and tracking MMS.
552 """
553
554 - def __init__(self, sandbox = "_Sandbox", realm = "BlueVia", version="v1"):
555 """
556 Initialize the BlueViaOutboundMms object
557
558 @param sandbox: (string): Indicates whether testing should be done in Sandbox mode. Use "" for real network access; Default: "_Sandbox",
559 @param realm: (string): Realm string; Default: "BlueVia"
560 @param version: (string): BlueVia API version; Default: "v1"
561 """
562
563 self.environment = sandbox
564 self.realm = realm
565 self.version = version
566 self.outbound_mms_url = "https://api.bluevia.com/services/REST/MMS%s/outbound/requests"
567
568
569 - def sendMMS(self, addresses, subject, messages, attachments):
570 """
571 Send MMS via BlueVia to one or more recipients
572
573 @param addresses: (array): An array of mobile numbers in the form "44 (for UK) 7764735478" (Mobile number without first zero and no spaces)
574 @param subject: (string): MMS subject text
575 @param messages: (array): An array containing the messages (string)
576 @param attachments: (array): An array of paths to files (string) to be sent with MMS
577
578 @return: (tuple): (HTTP status, deliveryURL). HTTP status == "201" for success. Use deliverURL in method deliverStatus.
579 """
580
581 assert self.hasCredentials(), "load oAuth credentials first or execute oAuth Dance"
582 assert (type(addresses) is ListType and addresses!=[]) or \
583 (type(addresses) is StringType and addresses!="") , "'addresses' must be a non empty string or a non empty array of strings"
584 assert type(subject) is StringType and subject!= "", "'subject' must be a non empty string"
585 assert type(messages) is ListType and messages!=[], "'messages' must be a non empty array of strings"
586 assert type(attachments) is ListType and attachments!=[], "'attachments' must be a non empty array of strings"
587
588 if type(addresses) == types.StringType:
589 addresses = [addresses]
590 addrlist = ",".join(['{"phoneNumber":"%s"}' % a for a in addresses])
591 if len(addresses) > 1: addrlist = '[%s]' % addrlist
592
593 root = '{"message": {"address": %s,"subject": "%s", "originAddress": {"alias": "%s"}}}' \
594 % (addrlist, subject, self.access_token.key)
595
596 body, headers = _encodeMultipart(root, messages, dict([[x,x] for x in attachments]))
597
598 parameters={"version":self.version, "alt":"json"}
599
600 response, content = self._signAndSend(self.outbound_mms_url % self.environment, "POST", self.access_token, \
601 parameters=parameters, body=body, extraHeaders=headers)
602 if response["status"] == '201':
603 return int(response["status"]), response["location"]
604 else:
605 return int(response["status"]), content
606
607
609 """
610 Track the delivery of a BlueVia MMS
611
612 @param deliveryURL: (string): deliveryURL provided by sendMMS method
613
614 @return: (tuple): (HTTP status, (dict) deliveryReceipt). HTTP status == "200" for success.
615 """
616
617 assert self.hasCredentials(), "load oAuth credentials first or execute oAuth Dance"
618 assert type(deliveryURL) is StringType and deliveryURL!= "", "'deliveryURL' must be a non empty string"
619
620 parameters = {"version":self.version, "alt":"json"}
621 response, content = self._signAndSend(deliveryURL, "GET", self.access_token, parameters=parameters)
622 if response["status"] == '200':
623 return int(response["status"]), simplejson.loads(content)["messageDeliveryStatus"]
624 else:
625 return int(response["status"]), content
626
627
628
629
630
631
633 """
634 The BlueVia class for receiving MMS and retrieving MMS attachments.
635 """
636
637 - def __init__(self, sandbox = "_Sandbox", realm = "BlueVia", version="v1"):
638 """
639 Initialize the BlueViaInboundMMS object
640
641 @param sandbox: (string): Indicates whether testing should be done in Sandbox mode. Use "" for real network access; Default: "_Sandbox"
642 @param realm: (string): Realm string; Default: "BlueVia"
643 @param version: (string): BlueVia API version; Default: "v1"
644 """
645
646 self.environment = sandbox
647 self.realm = realm
648 self.version = version
649 self.inbound_mms_url = "https://api.bluevia.com/services/REST/MMS%s/inbound/%s/messages"
650 self.attachments_mms_url = "https://api.bluevia.com/services/REST/MMS%s/inbound/%s/messages/%s"
651
652
654 """
655 Receive all MMS sent to the shortcode with the Keyword defined during BlueVia App generation
656
657 This method returns the message ids necessary to retrieve the MMS attachments
658
659 @param shortcode: (string): MMS shortcode including country code without "+", e.g. "44"
660
661 @return: (tuple): (HTTP status, (dict) receivedSMS). HTTP status == "200" for success.
662 """
663
664 assert self.hasCredentials(), "load oAuth credentials first or execute oAuth Dance"
665 assert type(shortcode) is StringType and shortcode!= "", "'shortcode' must be a non empty string"
666
667 url = self.inbound_mms_url % (self.environment, shortcode)
668 parameters = {"version":self.version, "alt":"json"}
669 response, content = self._signAndSend(url, "GET", self.access_token, parameters=parameters)
670 if response["status"] == '200':
671 return int(response["status"]), simplejson.loads(content)['receivedMessages']
672 else:
673 return int(response["status"]), content
674
675
677 """
678 Retrueve the MMS attachments
679
680 @param shortcode: (string): MMS shortcode including country code without "+", e.g. "44"
681 @param messageId: (string): The message Id recieved by receiveMMS method
682
683 @return: (tuple): (HTTP status, ((int) count, (string) folder)). HTTP status == "200" for success.
684 Writes <count> decoded files into <folder>
685 """
686
687 assert self.hasCredentials(), "load oAuth credentials first or execute oAuth Dance"
688 assert type(shortcode) is StringType and shortcode!= "", "'shortcode' must be a non empty string"
689 assert type(messageId) is StringType and messageId!= "", "'messageId' must be a non empty string"
690
691 url = self.attachments_mms_url % (self.environment, shortcode, messageId)
692 parameters = {"version":self.version, "alt":"json"}
693 response, content = self._signAndSend(url, "GET", self.access_token, parameters=parameters)
694
695 if response["status"] == '200':
696 return int(response["status"]), _decodeMultipart(messageId, content)
697 else:
698 return int(response["status"]), content
699
700
701
702
703
704
705
706 -class BlueViaUserContext(BlueVia):
707 """
708 The BlueVia class for retrieving information about user and terminal.
709 """
710
711 - def __init__(self, sandbox = "_Sandbox", realm = "BlueVia", version="v1"):
712 """
713 Initialize the BlueViaUserContext object
714
715 @param sandbox: (string): Indicates whether testing should be done in Sandbox mode. Use "" for real network access; Default: "_Sandbox"
716 @param realm: (string): Realm string; Default: "BlueVia"
717 @param version: (string): BlueVia API version; Default: "v1"
718 """
719
720 self.environment = sandbox
721 self.realm = realm
722 self.version = version
723 self.user_context_url = "https://api.bluevia.com/services/REST/Directory%s/alias:%s/UserInfo%s"
724
725
726 - def _getInfo(self, infoType, resultKey):
727 """
728 Internal method.
729 """
730
731 assert self.hasCredentials(), "load oAuth credentials first or execute oAuth Dance"
732
733 url = self.user_context_url % (self.environment, self.access_token.key, infoType)
734 parameters = {"version":self.version, "alt":"json"}
735 response, content = self._signAndSend(url, "GET", self.access_token, parameters=parameters)
736 if response["status"] == '200':
737 return int(response["status"]), simplejson.loads(content)[resultKey]
738 else:
739 return int(response["status"]), content
740
741
742 - def getUserInfo(self):
743 """
744 Retrieve all available info about the user.
745 Aggregates getPersonalInfo, getProfileInfo, getAccessInfo, getTerminalInfo
746
747 @return: (tuple): (HTTP status, (dict) userInfo). HTTP status == "200" for success.
748 """
749
750 return self._getInfo("", "userInfo")
751
752
753 - def getPersonalInfo(self):
754 """
755 Retrieve personal info about the user (depending on country, see www.bluevia.com)
756
757 @return: (tuple): (HTTP status, (dict) userPersonalInfo). HTTP status == "200" for success.
758 """
759
760 return self._getInfo("/UserPersonalInfo", "userPersonalInfo")
761
762
763 - def getProfileInfo(self):
764 """
765 Retrieve info about the user's profile (depending on country, see www.bluevia.com)
766
767 @return: (tuple): (HTTP status, (dict) userProfileInfo). HTTP status == "200" for success.
768 """
769
770 return self._getInfo("/UserProfile", "userProfile")
771
772
773 - def getAccessInfo(self):
774 """
775 Retrieve info about the user's access type (depending on country, see www.bluevia.com)
776
777 @return: (tuple): (HTTP status, (dict) userAccessInfo). HTTP status == "200" for success.
778 """
779
780 return self._getInfo("/UserAccessInfo", "userAccessInfo")
781
782
783 - def getTerminalInfo(self):
784 """
785 Retrieve info about the user's terminal (depending on country, see www.bluevia.com)
786
787 @return: (tuple): (HTTP status, (dict) userTerminalInfo). HTTP status == "200" for success.
788 """
789
790 return self._getInfo("/UserTerminalInfo", "userTerminalInfo")
791
792
793
794
795
796
798 """
799 The BlueVia class for retrieving user's terminal cell location.
800 """
801
802 - def __init__(self, sandbox = "_Sandbox", realm = "BlueVia", version="v1"):
803 """
804 Initialize the BlueViaLocation object
805
806 @param sandbox: (string): Indicates whether testing should be done in Sandbox mode. Use "" for real network access; Default: "_Sandbox"
807 @param realm: (string): Realm string; Default: "BlueVia"
808 @param version: (string): BlueVia API version; Default: "v1"
809 """
810
811 self.environment = sandbox
812 self.realm = realm
813 self.version = version
814 self.location_url = "https://api.bluevia.com/services/REST/Location%s/TerminalLocation"
815
816
818 """
819 Retrieve the cell location of user's terminal (mobile device) including accuracy information
820
821 Parameter:
822 @param accuracy: (int): Provide neccessary accurracy. Returns an error if accuracy requirements are not met.
823
824 @return: (tuple): (HTTP status, (dict) locationData). HTTP status == "200" for success.
825 """
826
827 assert self.hasCredentials(), "load oAuth credentials first or execute oAuth Dance"
828
829 url = self.location_url % (self.environment)
830 parameters = {"version":self.version, "alt":"json", "locatedParty":"alias:" + self.access_token.key }
831 if accuracy:
832 parameters["acceptableAccuracy"] = str(accuracy)
833 response, content = self._signAndSend(url, "GET", self.access_token, parameters=parameters)
834 if response["status"] == '200':
835 return int(response["status"]), simplejson.loads(content)
836 else:
837 return int(response["status"]), content
838
839
840
841
842
843
844
846 """
847 The BlueVia class for receiving BlueVia ads.
848 """
849
850 - def __init__(self, adspaceId, sandbox = "_Sandbox", realm = "BlueVia", version="v1"):
851 """
852 Initialize the BlueViaAds object
853
854 @param adspaceId: The BlueVia ad space id provide during BlueVia app generation
855 @param sandbox: (string): Indicates whether testing should be done in Sandbox mode. Use "" for real network access; Default: "_Sandbox"
856 @param realm: (string): Realm string; Default: "BlueVia"
857 @param version: (string): BlueVia API version; Default: "v1"
858 """
859
860 assert type(adspaceId) is StringType and adspaceId!= "", "'adspaceId' must be a non empty string"
861
862 self.environment = sandbox
863 self.realm = realm
864 self.version = version
865 self.adspaceId = adspaceId
866 self.simple_ads_url = "https://api.bluevia.com/services/REST/Advertising%s/simple/requests"
867
868
869 - def getAd_2l(self, country, targetUserId=None, textAd=False, userAgent='none', keywordList=None, protectionPolicy=1):
870 """
871 Get ads without user authorization (2-legged oAuth)
872
873 @param country: (string): User's country in standard abbreviation, e.g. "UK"
874
875 @param targetUserId: (string): Unique id to avoid users to be presented with the same Ad several times. Default: None,
876 @param textAd: (string): If true requestes text ads, else image ads; efault is False
877 @param userAgent: (string): Allows BlueVia advertising service to return the more appropiate size. Default: 'none' (and not python None)
878 @param keywordList: (array): Get an ad related to some topics (e.g. 'computer|laptop'). Default: None,
879 @param protectionPolicy: (int): 1: Low, moderately explicit content 2:Safe, not rated content 3:High, explicit content. Default: 1
880
881 @return: (tuple): (HTTP status, (dict) advertisingData). HTTP status == "201" for success.
882 """
883
884 assert type(country) is StringType and country!= "", "'country' must be a non empty string"
885
886 return self._getAd(None, country, targetUserId, textAd, userAgent, keywordList, protectionPolicy)
887
888
889 - def getAd_3l(self, textAd=False, userAgent='none', keywordList=None, protectionPolicy=1):
890 """
891 Get ads without user authorization (2-legged oAuth)
892
893 @param textAd: (string): If true requestes text ads, else image ads; efault is False
894 @param userAgent: (string): Allows BlueVia advertising service to return the more appropiate size. Default: 'none' (and not python None)
895 @param keywordList: (array): Get an ad related to some topics (e.g. 'computer|laptop'). Default: None,
896 @param protectionPolicy: (int): 1: Low, moderately explicit content 2:Safe, not rated content 3:High, explicit content. Default: 1
897
898 @return: (tuple): (HTTP status, (dict) advertisingData). HTTP status == "201" for success.
899 """
900
901 assert self.hasCredentials(), "load oAuth credentials first or execute oAuth Dance"
902
903 return self._getAd(self.access_token, None, None, textAd, userAgent, keywordList, protectionPolicy)
904
905
906 - def _getAd(self, token, country, targetUserId, textAd, userAgent, keywordList, protectionPolicy):
907 """
908 Internal method
909 """
910
911 parameters = {}
912 parameters["ad_request_id"] = str(uuid.uuid4()) + time.asctime()
913 if textAd:
914 parameters["ad_presentation"] = '0104'
915 else:
916 parameters["ad_presentation"] = '0101'
917 if country:
918 parameters["country"] = country
919 if targetUserId:
920 parameters["target_user_id"] = targetUserId
921
922 parameters["ad_space"] = self.adspaceId
923 parameters["user_agent"] = userAgent
924 if keywordList:
925 parameters["keywords"] = "|".join(keywordList)
926 parameters["protection_policy"] = protectionPolicy
927
928
929 body = urllib.urlencode(parameters)
930
931 parameters["version"] = self.version
932
933 response, content = self._signAndSend(self.simple_ads_url % self.environment, "POST", token, \
934 parameters=parameters, body=body, is_form_encoded=True, \
935 extraHeaders={"Content-Type":"application/x-www-form-urlencoded;charset=UTF8"})
936
937 if response["status"] == '201':
938 return (int(response["status"]), _parseAdResponse(content))
939 else:
940 return response, content
941
942
943
944
945
947 """
948 The BlueVia class for payments.
949 """
950
951 - def __init__(self, consumer_key, consumer_secret, sandbox = "_Sandbox", realm = "BlueVia", version="v1"):
952 """
953 Initialize the BlueViaPayment object
954
955 @param consumer_key: (string): Key of the Consumer Credentials
956 @param consumer_secret: (string): Secret of the Consumer Credentials
957 @param sandbox: (string): Indicates whether testing should be done in Sandbox mode. Use "" for real network access; Default: "_Sandbox"
958 @param realm: (string): Realm string; Default: "BlueVia"
959 @param version: (string): BlueVia API version; Default: "v1"
960 """
961
962 BlueViaOauth.__init__(self, consumer_key, consumer_secret)
963 self.environment = sandbox
964 self.realm = realm
965 self.version = version
966 self.payment_url = "https://api.bluevia.com/services/RPC/Payment%s/payment"
967 self.payment_status_url = "https://api.bluevia.com/services/RPC/Payment%s/getPaymentStatus"
968 self.payment_cancel_url = "https://api.bluevia.com/services/RPC/Payment%s/cancelAuthorization"
969
971 """
972 First call of the Payment oAuth Dance. Provide the Consumer Credential and request the one time Request Token
973 (Override of BlueViaOauth fetch_request_token method)
974
975 @param amount: (string): Price in the form 125 for 1.25
976 @param currency: (string): Currency, e.g. "EUR", "GBP"
977 @param serviceId: (string): Product identifier provided by our Mobile Payments Partner (sandbox: free choice)
978 @param serviceName: (string): Product name as registered at our Mobile Payments Partner (sandbox: free choice)
979 @param callback: (string): The callback URL or "oob". Default: "oob"
980
981 @return: (tuple): (HTTP status, authorization URL). HTTP status == "200" for success
982 """
983
984 assert type(amount) is IntType and amount > 0, "'amount' must be an Integer > 0"
985 assert type(currency) is StringType and currency!= "", "'currency' must be a non empty string"
986 assert type(serviceId) is StringType and serviceId!= "", "'serviceId' must be a non empty string"
987 assert type(serviceName) is StringType and serviceName!= "", "'serviceName' must be a non empty string"
988
989 self.amount = amount
990 self.currency = currency
991 self.serviceId = serviceId
992 self.serviceName = serviceName
993 self.correlator = str(uuid.uuid4())
994
995 paymentInfo = {"paymentInfo.currency":currency, "paymentInfo.amount":amount}
996 serviceInfo = {"serviceInfo.name":serviceName, "serviceInfo.serviceID":serviceId}
997
998 parameters={"oauth_callback":callback, "xoauth_apiName":"%s%s" % ("Payment",self.environment)}
999 parameters.update(paymentInfo)
1000 parameters.update(serviceInfo)
1001
1002 body = "%s&%s" % (urllib.urlencode(paymentInfo), urllib.urlencode(serviceInfo))
1003 body = body.replace("+", "%20")
1004
1005 response, content = self._signAndSend(self.request_token_url, "POST", None, \
1006 parameters=parameters, \
1007 body=body, is_form_encoded=True, \
1008 extraHeaders={"Content-Type":"application/x-www-form-urlencoded;charset=UTF8"})
1009 if response["status"] == '200':
1010 self.request_token = oauth.Token.from_string(content)
1011 return int(response["status"]), "%s?oauth_token=%s" % (self.authorization_url, self.request_token.key)
1012 else:
1013 return int(response["status"]), content
1014
1015
1017 """
1018 Save Access Token and payment info (amount, currency, serviceId, serviceName, correlator)
1019 Will be valid for 48h only!
1020
1021 Note: Unencrypted storage. Use only during development
1022
1023 @param path: (string): Path to file on disk (pickle file)
1024 """
1025
1026 assert type(path) is StringType and path!= "", "'path' must be a non empty string"
1027
1028 fd = open(path, "w")
1029 data = (self.consumer, self.access_token, self.amount, self.currency, \
1030 self.serviceId, self.serviceName, self.correlator)
1031 pickle.dump(data, fd)
1032 fd.close()
1033
1034
1036 """
1037 Load Access Token and payment info (amount, currency, serviceId, serviceName, correlator)
1038
1039 @param path: (string): Path to file on disk (pickle file)
1040 """
1041
1042 assert type(path) is StringType and path!= "", "'path' must be a non empty string"
1043
1044 if os.path.exists(path):
1045 fd = open(path, "r")
1046 self.consumer, self.access_token, self.amount, self.currency, \
1047 self.serviceId, self.serviceName, self.correlator = pickle.load(fd)
1048 fd.close()
1049 return True
1050 else:
1051 return False
1052
1053
1055 """
1056 Issue the actual payment with amount, currency, serviceId, serviceName given by fetch_request_token method
1057
1058 @return: (tuple): (HTTP status, (dict) paymentStatus). HTTP status == "200" for success.
1059 """
1060 assert self.hasCredentials(), "load oAuth credentials first or execute oAuth Dance"
1061 assert type(self.amount) is IntType and self.amount > 0, "'amount' must be an Integer > 0"
1062 assert type(self.currency) is StringType and self.currency!= "", "'currency' must be a non empty string"
1063
1064 p = {"methodCall": {
1065 "id": self.correlator,
1066 "version": self.version,
1067 "method": "PAYMENT",
1068 "params": { "paymentParams": {"paymentInfo": { "amount": str(self.amount),
1069 "currency": self.currency },
1070 "timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ") } }
1071 }}
1072 body = simplejson.dumps(p)
1073
1074 response, content = self._signAndSend(self.payment_url % self.environment, "POST", self.access_token, \
1075 parameters={}, body=body, \
1076 extraHeaders={"Content-Type":"application/json;charset=UTF8"})
1077 if response["status"] == '200':
1078 return int(response["status"]), simplejson.loads(content)
1079 else:
1080 return int(response["status"]), content
1081
1082
1084 """
1085 Check the Payment status (polling)
1086
1087 @param transactionId: (string): Transaction Id provided by issuePayment method
1088
1089 @return: (tuple): (HTTP status, (dict) paymentStatus). HTTP status == "200" for success.
1090 """
1091
1092 assert self.hasCredentials(), "load oAuth credentials first or execute oAuth Dance"
1093 assert type(transactionId) is StringType and transactionId!= "", "'transactionId' must be a non empty string"
1094
1095 p = {"methodCall": {
1096 "id": self.correlator,
1097 "version": self.version,
1098 "method": "GET_PAYMENT_STATUS",
1099 "params": { "getPaymentStatusParams": { "transactionId": transactionId} }
1100 }}
1101
1102 body = simplejson.dumps(p)
1103
1104 response, content = self._signAndSend(self.payment_status_url % self.environment, "POST", self.access_token, \
1105 parameters={}, body=body, \
1106 extraHeaders={"Content-Type":"application/json;charset=UTF8"})
1107
1108 if response["status"] == '200':
1109 return int(response["status"]), simplejson.loads(content)
1110 else:
1111 return int(response["status"]), content
1112
1113
1115 """
1116 Check the Payment status (polling)
1117
1118 @param correlator: (string): correlator provided by issuePayment method
1119
1120 @return: (tuple): (HTTP status, (dict) paymentStatus). HTTP status == "200" for success.
1121 """
1122
1123 assert self.hasCredentials(), "load oAuth credentials first or execute oAuth Dance"
1124 assert type(correlator) is StringType and correlator!= "", "'correlator' must be a non empty string"
1125
1126 p = {"methodCall": {
1127 "id": self.correlator,
1128 "version": self.version,
1129 "method": "CANCEL_AUTHORIZATION"
1130 }}
1131
1132 body = simplejson.dumps(p)
1133
1134 response, content = self._signAndSend(self.payment_cancel_url % self.environment, "POST", self.access_token, \
1135 parameters={}, body=body, \
1136 extraHeaders={"Content-Type":"application/json;charset=UTF8"})
1137
1138 if response["status"] == '200':
1139 return int(response["status"]), simplejson.loads(content)
1140 else:
1141 return int(response["status"]), content
1142