/
14.markmin
646 lines (468 loc) · 31.3 KB
/
14.markmin
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
## Otras Recetas
### Upgrade
``upgrades``:inxx
En la página de la interfaz administrativa "site" existe un botón "upgrade now" (actualice la versión ahora). En caso que no esté disponible o no funcione (por ejemplo por problema de bloqueo de un archivo), actualizar web2py manualmente es muy fácil.
-------
Simplemente descomprime la última versión de web2py sobre la vieja instalación.
-------
Esto actualizará todas las librerías así como también las aplicaciones **admin**, **examples**, **welcome**. Además creará un nuevo archivo vacío "NEWINSTALL". Al reiniciar, web2py eliminará los archivos vacíos y empaquetará la aplicación "welcome" en "welcome.w2p". Esta última será usada como la nueva estructura de la aplicación.
web2py no actualiza cualquier otro archivo existente en otra aplicación.
### Cómo distribuir tus aplicaciones como binarios
Es posible empaquetar tus aplicaciones con la distribución binaria de web2py y distribuirlas juntas. La licencia lo permite, siempre y cuando dejes claro en la licencia de tu aplicación que tu estas agregando web2py y añadas un enlace a ``web2py.com``.
Aquí explicamos como hacer esto para Windows:
- Crea tu aplicación como usalmente lo haces.
- Usando **admin**, compila tu aplicación(one click)
- Usando **admin**, empaqueta la aplicación compilada(another click)
- Descomprime esto en la carpeta "miapp" y comienza ahí (two clicks)
- Carga usando **admin** el anterior paquete de la app compilada con el nombre "init" (one click)
- Crea un archivo "miapp/start.bat" que contenga "cd web2py; web2py.exe"
- comprime la carpeta miaplicacion en un archivo "miapp.zip"
- distribuye y/o vende "miapp.zip"
Cuando los usuarios descompriman "miapp.zip" y hagan click "run" ellos verán tu aplicación en lugar de la aplicación "welcome".
No tiene requerimientos del lado del usuario, ni incluso tener python pre-instalado.
Para binarios en Mac el proceso es el mismo pero no hay necesidad de crear el archivo "bat".
### WingIDE, Rad2Py, y Eclipse
``WingIDE``:inxx ``Eclipse``:inxx ``Rad2Py``:inxx
Puedes usar web2py con entornos de desarrollo de terceros como WingIDE, Rad2Py, y Eclipse.
He aquí una captura de pantalla que ilustra el uso de web2py con WingIDE:
[[image @///image/wingide.png center 480px]]
En general, el problema con estos IDE (salvo el caso de Rad2Py que fue diseñado específicamente para que funciones con web2py) es que no entienden el contexto en que los modelos y controladores se ejecutan y por lo tanto la autocompleción no funciona en forma instantánea.
Para que la autocompleción funcione el truco normalmente consiste en editar tus modelos y controladores agregando el siguiente código:
``
if False:
from gluon import *
request = current.request
response = current.response
session = current.session
cache = current.cache
T = current.T
``:code
Esto no modifica el funcionamiento ya que nunca se ejecutará pero hace que el IDE lo analice y entienda de dónde vienen los objetos del espacio de nombres global (el módulo ``gluon``), lo que a su vez hace que funcione la autocompleción.
### SQLDesigner
Hay un software llamado SQLDesigner que permite la creación de modelos de web2py en forma visual y con esa información se genera el código automáticamente. Aquí se muestra una captura de pantalla:
[[image @///image/designer.png center 480px]]
La versión de SQLDesigner que funciona con web2py se puede encontrar en:
``https://github.com/elcio/visualdal``
### Publicación de una carpeta
Consideremos el problema de compartir carpetas y subcarpetas en la web. web2py lo hace realmente fácil. Sólo debes usar un controlador como el siguiente:
``
from gluon.tools import Expose
def micarpeta():
return dict(archivos=Expose('/ruta/a/micarpeta'))
``:code
que puedes procesar en una vista con ``{{=archivos}}``. Esto creará una interfaz para visualizar archivos y carpetas, y navegar a través de la estructura de árbol de los archivos. Se crearán vistas previas de las imágenes.
El prefijo con la ruta "/ruta/a/micarpeta" se ocultará a los visitantes. Por ejemplo un archivo llamado "/ruta/a/micarpeta/a/b.txt" se reemplazará por "base/a/b.txt". El prefijo "base" se puede especificar usando el argumento ``basename`` de la función Expose. Puedes especificar una lista de extensiones a listar con el parámetro ``extensions``, el resto de los archivos se ocultará. Por ejemplo:
``
def micarpeta():
return dict(archivos=Expose('/ruta/a/micarpeta', basename='.',
extensions=['.py', '.jpg']))
``:code
Los archivos y carpetas que contengan la palabra "private" en la ruta o tengan un nombre de archivo que comience con "." o que termine con "~" siempre se ocultan.
### Pruebas funcionales
web2py viene con un módulo ``gluon.contrib.webclient`` que permite realizar pruebas funcionales (''functional testing'') de aplicaciones locales y remotas de web2py. En realidad, este módulo no es específico de web2py y se puede usar para realizar pruebas e interacción en forma programática con cualquier aplicación web, si bien está preparado para trabajar con sesiones y repuestas de aplicaciones de web2py.
He aquí un ejemplo de uso. El programa a continuación crea un cliente, conecta con la acción "index" para establecer una sesión, registra un nuevo usuario, luego cierra la sesión y vuelve a autenticarse usando las nuevas credenciales:
``
from gluon.contrib.webclient import WebClient
cliente = WebClient('http://127.0.0.1:8000/welcome/default/',
postbacks=True)
cliente.get('index')
# registro
datos = dict(first_name = 'Homero',
last_name = 'Simpson',
email = 'homero@web2py.com',
password = 'prueba',
password_two = 'prueba',
_formname = 'register')
cliente.post('user/register', data=datos)
# cerrar sesión
cliente.get('user/logout')
# autenticarse nuevamente
datos = dict(email='homero@web2py.com',
password='prueba',
_formname = 'login')
cliente.post('user/login',data=datos)
# comprobar registro y si el acceso fue exitoso
cliente.get('index')
assert('Bienvenido Homero' in cliente.text)
``:code
El constructor de WebClient toma un prefijo URL como argumento. En el ejemplo ese parámetro es "http://127.0.0.1:8000/welcome/default/". No realiza ningún intercambio de datos en la red. El argumento ``postbacks`` es por defecto ``True`` y le indica al cliente cómo debe manejar las respuestas de web2py.
El objeto WebClient ``client`` tiene dos métodos:
``get`` y ``post``. El primer argumento siempre es un postfijo URL.
El URL completo para una solicitud POST o GET se compone simplemente concatenando el prefijo y el postfijo. El propósito de esto es simplemente hacer que la sintaxis sea más breve para las comunicaciones intensivas entre el cliente y el servidor.
``data`` es un parámetro específico de las solicitudes POST que contiene un diccionario de los datos a enviarse. Los formularios de web2py tienen un campo oculto ``_formname`` y su valor se debe especificar cuando existe más de un formulario en la página. Los formularios de web2py también contienen un campo oculto ``_formkey`` que está diseñado para prevenir los ataques CSRF. Este campo es manejado en forma automática por el WebClient.
Tanto ``cliente.get`` como ``cliente.post`` aceptan los siguientes argumentos adicionales:
- ``headers``: un diccionario de encabezados HTTP opcionales.
- ``cookies``: un diccionario de cookies opcionales HTTP.
- ``auth``: un diccionario de parámetros que se deben pasar a ``urllib2.HTTPBasicAuthHandler().add_password(**auth)`` para el acceso por el sistema de autenticación básico (''basic authentication''). Para más información acerca de este tema se puede consultar la documentación de Python para el módulo urllib2.
El objeto ``client`` en el ejemplo mantiene una conversación con el servidor especificado en el constructor por medio de solicitudes GET y POST. Este maneja automáticamente las cookie y las envía de regreso para actualizar las sesiones. Si detecta que se ha generado una nueva cookie de sesión aunque se esté usando actualmente otra, interpretará que se produjo una falla en la sesión y genera una excepción. Si el servidor devuelve un error HTTP, también generará una excepción. Si, en cambio, el servidor devuelve un error HTTP, pero además la respuesta contiene un ticket de web2py, devolverá una excepción conteniendo el código del ticket.
El objeto client mantiene un registro o ''log'' de las solicitudes en ``cliente.history`` y el estado asociado con la última solicitud exitosa.
El estado se compone de:
- ``cliente.status``: el código de estado devuelto
- ``cliente.text``: el contenido de la página
- ``cliente.headers``: un diccionario de encabezados recuperados
- ``cliente.cookies``: un diccionario de los cookie recuperados
- ``cliente.sessions``: un diccionario de sesiones con la forma ``{nombredeapp: id_sesión}``.
- ``cliente.forms``: un diccionario de formularios web2py detectados en el ``cliente.text``. El diccionario tiene la forma ``{_formname, _formkey}``.
El objeto WebClient no realiza ningún análisis o conversión del contenido del ``cliente.txt`` devuelto por el servidor pero esto se puede hacer fácilmente con alguno de los módulos de terceros como BeautifulSoup. Por ejemplo, aquí se muestra un programa que demuestra cómo se pueden buscar todos los link en una página descargada por el cliente y comprobarlos:
``
from BeautifulSoup import BeautifulSoup
dom = BeautifulSoup(cliente.text)
for link in dom.findAll('a'):
nuevo_cliente = WebClient()
nuevo_cliente.get(link.href)
print nuevo_cliente.status
``:code
### Construyendo un web2py minimalista
A veces necesitamos implementar web2py en un servidor con muy poca memoria RAM. En este caso nosotros queremos disminuir web2py a su mínima expresión.
Una sencilla manera de hacer esto es la siguiente:
- En una máquina de producción, instala web2py desde las fuentes.
- Desde adentro de la carpeta principal de web2py ejecuta
``
python scripts/make_min_web2py.py /path/to/minweb2py
``
- Ahora copia sobre "/path/to/minweb2py/applications" la aplicación que quieres implementar
- Implementa "/path/to/minweb2py" a el pequeño servidor.
El script "make_min_web2py.py" creará una distribución mínima de web2py que no incluye:
- admin
- examples
- welcome
- scripts
- raramente usados modulos contrib
Esto incluye una aplicación "welcome" que consiste de un archivo para permitir la implementación de pruebas.
Mira este script. Al principio contiene una lista detallada de qué está incluido y qué es ignorado. Tu puedes fácilmente modificarlo y ajustar a tus necesidades.
### Obteniendo una URL externa
``fetch``:inxx
Python incuye la librería ``urllib`` para descargar recursos desde un url:
``
import urllib
page = urllib.urlopen('http://www.web2py.com').read()
``:code
``API``:inxx
Esto es usualmente correcto, pero el modulo ``urllib`` no funciona sobre "Google App Engine". Google provee una diferente API para descargar URLs que solamente funciona en GAE. en orden de hacer tu código portable, web2py incluye una función ``fetch`` que funcióna en GAE así mismo en otras instalaciones de Python
``
from gluon.tools import fetch
page = fetch('http://www.web2py.com')
``:code
### Pretty date
``prettydate``:inxx
Es usualmente útil para representar un datetime no como "2009-07-25 14:34:56" pero como "hace un año". web2py proporciona una función para esto:
``
import datetime
d = datetime.datetime(2009,7,25,14,34,56)
from gluon.tools import prettydate
pretty_d = prettydate(d,T)
``:code
El segundo argumento (T) debe ser pasado a través de la internacionalización para la salida.
### Geocoding
``geocode``:inxx
Si necesitas convertir una dirección (por ejemplo: "243 S Wabash Ave, Chicago, IL, USA") en coordenadas geográficas (latitud y longitud), web2py provee una función para realizar eso, entonces.
``
from gluon.tools import geocode
address = '243 S Wabash Ave, Chicago, IL, USA'
(latitude, longitude) = geocode(address)
``:code
La función ``geocode`` require una conexión a internet además se conecta al servicio de Google de geocodificación. La función retorna ``(0,0)`` en caso de fallar. Nota que el servicio de Google de geocodificación tiene un número máximo de peticiones, entonces debes verificar sus términos y condiciones. La función ``geocode`` es implementada al comienzo de la función ``fetch`` y por esto funcióna en GAE.
### Paginación
``pagination``:inxx
Esta receta es un truco útil para minimizar el acceso a base de datos en el caso de la paginación, e.g., cuando necesitas mostrar una lista de filas desde una base de datos pero quieres distribuir las filas sobre múltiples páginas.
Comienza creando una aplicación **primos** que almacene los primeros 1000 números primos en una base de datos.
Acá está el modelo ``db.py``:
``
db = DAL('sqlite://primos.db')
db.define_table('primos',Field('valor','integer'))
def esprimo(p):
for i in range(2,p):
if p%i==0: return False
return True
if len(db().select(db.prime.id))==0:
p=2
for i in range(1000):
while not esprimo(p): p+=1
db.primo.insert(value=p)
p+=1
``:code
Ahora crea una acción ``list_items`` in the "default.py" en el controlador que se lea de esta forma:
``
def listar_elementos():
if len(request.args): pagina=int(request.args[0])
else: pagina=0
elementos_por_pagina=20
limitby=(pagina*elementos_por_pagina,(pagina+1)*elementos_por_pagina+1)
registros=db().select(db.primo.ALL,limitby=limitby)
return dict(registros=registros,pagina=pagina,
elementos_por_pagina=elementos_por_pagina)
``:code
Nota que este código selecciona uno o más item de los que necesita, 20+1. el elemento extra dice a la vista si existe la siguiente página.
Aquí es la vista "default/listar_elementos.html":
``
{{extend 'layout.html'}}
{{for i,registro in enumerate(registros):}}
{{if i==elementos_por_pagina: break}}
{{=registro.valor}}<br />
{{pass}}
{{if pagina:}}
<a href="{{=URL(args=[pagina-1])}}">previa</a>
{{pass}}
{{if len(registros)>items_per_page:}}
<a href="{{=URL(args=[pagina+1])}}">próxima</a>
{{pass}}
``:code
De esta manera podemos obtener la paginación con un sólo "select" por acción, y que ese "select" sólo obtenga una fila más de lo que se necesita.
### httpserver.log y el Formato del archivo de bitácora.
``httpserver.log``:inxx
El servidor de web2py registra todas las peticiones en un archivo llamado:
``
httpserver.log
``:code
en el directorio raíz de web2py. Un nombre alternativo y ubicación puede ser especificado a través de las opciones de la línea de comandos de web2py.
Se añaden nuevas entradas al final del archivo cada vez que una peticón es realizada. Cada línea luce como esto:
``
127.0.0.1, 2008-01-12 10:41:20, GET, /admin/default/site, HTTP/1.1, 200, 0.270000
``:code
El formato es:
``
ip, timestamp, method, path, protocol, status, time_taken
``:code
Donde
- ip es la dirección IP del cliente que hico la petición
- timestamp es la fecha y la hora de la petición en el formato ISO 8601, YYYY-MM-DDT HH:MM:SS
- method bien sea GET or POST
- path es la ruta solicitada por el cliente
- protocol es la versión del protocolo HTTP usado para enviar al cliente, usually HTTP/1.1
- status es el código de estado del protocolo HTTP ``status``:cite
- time_taken es la cantidad de tiempo que el servidor tarda en procesar la petición, en segundos, no incluye tiempos de upload/download.
En el repositorio de aplicaciones ``appliances``:cite , podrás encontrar una aplicación para el análisis de logs (registros del sistema).
Estos registros de logs estan deshabilitado por defecto cuando usas mod_wsgi a partir de esto podría ser igual como el log de Apache.
### Llenando la Base de datos con dummy data.
Para propósitos de pruebas es conveniente ser capaz de llenar la base de datos con información aleatoria. web2py incluye un clasificador bayesiano entrenado y listo para generar textos legibles para este propósito.
Esta es la forma más simple de usar:
``
from gluon.contrib.populate import populate
populate(db.mitabla,100)
``:code
Esto insertará 100 registros aleatorios en db.mitabla, e intentará hacer inteligentemente la generación de textos cortos para campos que sean cadenas, enteros, double, date, datetime, time, booleanos, etc. para el campo correspondiente. Intentará respetar los requerimientos impuestos por los validadores. Para campos que contienen la palabra "name" , intentará generar nombres dummy. Para campos reference se generarán referencias válidas.
Si tienes dos tablas (A y B) donde B hace referencia a A, comprueba llenar primero A y luego B.
Como las inserciones son hechas en una transacción, no intentes insertar muchísimos registros al mismo tiempo, particularmente si estan involucradas referencias. en su lugar, has un ciclo, insertando 100 cada vez, y luego un commit().
``
for i in range(10):
populate(db.mitabla,100)
db.commit()
``:code
También puedes usar el clasificador bayesiano para aprender características de algunos textos y generar textos aleatorios que sean similares, pero podría no tener sentido:
``
from gluon.contrib.populate import Learner, IUP
ell=Learner()
ell.learn('una entrada de texto realmente larga ...')
print ell.generate(1000,prefix=None)
``:code
### Aceptando pagos con tarjetas de crédito
``Google Wallet``:inxx ``Paypal``:inxx ``Stripe.com``:inxx
``Authorize.net``:inxx ``DowCommerce``:inxx
Existen múltiples maneras de aceptar pagos con tarjetas de crédito por internet. web2py proporciona una API especifica para algunos de los más populares y prácticos:
- Google Wallet ``googlewallet``:cite
- PayPal ``paypal``cite
- Stripe.com ``stripe``:cite
- Authorize.net ``authorizenet``:cite
- DowCommerece ``dowcommerce``:cite
Los primeros dos mecanismos delegan el proceso de autenticación del pago en un servicio de terceros. Mientras esta es la mejor solución de seguridad (tu aplicación no tiene que manejar ninguna información de tarjetas de crédito) esto hace el proceso incómodo (el usuario debe loguearse dos veces; por ejemplo, una vez para tu aplicación, y otra con Google) y no permite que tu aplicación maneje pagos sucesivos en una forma automatizada.
Hay veces que necesitas más control y quieres generar el formulario de entrada para la información de las tarjetas de crédito y que programaticalmente pregunte a quién lo procesa cómo transferir el dinero desde la tarjeta de crédito a tu cuenta.
Por esta razón web2py provee integración lista para usar con Stripe, Authorize.net (el modulo fue desarrollado por John Conde y ligeramente modificado) y DowCommerce. Stripe es la aplicación más simple de usar y también la más económica para bajo volumen de transacciones (No cobran un cargo fijo pero el costo por transacción está sobre el 3%). Authorize.net es mejor para altos volúmenes (tiene una tarifa fija anual más un bajo costo por transacción).
Ten en cuenta que en el caso de Stripe y Authorize.net tu programa estará aceptando la información de las tarjetas de crédito. No tienes que almacenar esta información y nosotros te aconsejamos no hacerlo, por los requerimientos legales que involucra (verificar con Visa o Mastercard) pero hay ocasiones en las que tu puedes querer almacenar la información para pagos sucesivos o reproducir el botón de pago de un sólo click de Amazon.
#### Google Wallet
La manera más sencilla de usar Google Wallet (Level 1) consiste en integrar un botón a tu página y cuando hagan click, redirigir a tus visitantes a la página de pagos provista por Google.
Primero que todo necesitas registrar una cuenta de Google Merchant en la siguiente url:
``
https://checkout.google.com/sell
``
Necesitarás indicarle a Google tu información bancaria. Google te asignará un identificador ``merchant_id`` y una clave ``merchant_key`` (no los confundas, mantenlos en secreto).
Entonces simplemente necesitas crear el siguiente código en tu vista:
``
{{from gluon.contrib.google_wallet import button}}
{{=button(merchant_id="123456789012345",
products=[dict(name="shoes",
quantity=1,
price=23.5,
currency='USD',
description="running shoes black")])}}
``
Cuando un visitante hace click en el botón, el visitante será redireccionado a la página de google donde ella o él pueden pagar por los artículos. Aquí productos son una lista de productos y cada producto es un diccionario de parámetros que quieres pasar describiendo tus artículos (nombre, cantidad, precio, moneda, descripción, y otros opcionales que puedes encontrar descriptos en la documentación de Google Wallet).
Si eliges usar este mecanismo, debes generar los valores pasados al botón programado con información basada en tu inventario y del carrito de compra del visitante.
Todas la información de tasas y tarifas será manejada del lado de Google. Lo mismo vale para la información de la cuenta. Por defecto tu aplicación no es informada si la transacción ha sido completada sin embargo podrías tener que visitar tu cuenta de Google Merchant para ver cuáles productos han sido vendidos y pagado por ellos, y cuáles productos necesitan ser enviados a sus compradores. también Google te enviará un email con la información.
Si quieres una integración más estricta tienes que usar el nivel 2 de notificaciones de la API. En ese caso puedes pasar más información a Google y Google llamará a tu API para notificar las ventas. Esto permite mantener la información de la cuenta dentro de tu aplicación pero esto requiere que tengas servicios web que puedan comunicarse con Google Wallet.
Esto último podría llegar a resultar complicado de programar para una app determinada, pero ya disponemos de un plugin que implementa una API para este propósito.
``
http://web2py.com/plugins/static/web2py.plugin.google_checkout.w2p
``
Puedes encontrar información sobre la documentación del plugin dentro del plugin.
#### Paypal
La integración con Paypal no es descrita aquí pero puedes encontrar más información en este enlace (recurso):
``
http://www.web2pyslices.com/main/slices/take_slice/9
``
#### Stripe.com
Esta es probablemente uno de las más fáciles y flexibles maneras de aceptar pagos con tarjetas de crédito.
Necesitas registrar con Stripe.com y eso es un proceso muy fácil, de hecho Stripe te asignará una llave de la API para pruebas antes de que crees cualquier credencial.
Una vez que tienes tu llave de la API, puedes aceptar tarjetas de crédito, con el siquiente código:
``
from gluon.contrib.stripe import Stripe
stripe = Stripe(api_key)
d = stripe.charge(amount=100,
currency='usd',
card_number='4242424242424242',
card_exp_month='5',
card_exp_year='2012',
card_cvc_check='123',
description='the usual black shoes')
if d.get('paid',False):
# payment accepted
elif:
# error is in d.get('error','unknown')
``
La respuesta, ``d``, es un diccionario que puedes explorar tu mismo. El número de tarjeta usado en el ejemplo es un sandbox y siempre será exitoso. Cada transacción está asociada a un identificador almacenado en ``d['id']``.
Stripe también permite verificar una transaccion en otro momento:
``
d = Stripe(key).check(d['id'])
``
y una transacción de reembolso:
``
r = Stripe(key).refund(d['id'])
if r.get('refunded',False):
# el reembolso se realizó con éxito
elif:
# error is in d.get('error','unknown')
``
Stripe hace sencillo mantener la contabilidad dentro de tu aplicación.
todas las comunicaciones entre tu aplicación y Stripe se realizan sobre servicios web RESTful. Stripe actualmente expone aún más servicios y proporciona un conjunto más amplio en la API de Python. Puedes leer más sobre esto en su sitio web.
#### Authorize.Net
Otra simple manera de aceptar tarjetas de crédito es usar Authorize.Net. Como es usual, necesitas registrarte y obtener un ``login`` y una llave para las transacciones (``transkey``). Una vez que tengas esto, funciona muy similar en espíritu como lo hace Stripe:
``
from gluon.contrib.AuthorizeNet import process
if process(creditcard='4427802641004797',
expiration="122012,
total=100.0,cvv='123',tax=None,invoice=None,
login='cnpdev4289', transkey='SR2P8g4jdEn7vFLQ',testmode=True):
# el pago se ha procesado
else:
# el pago se ha rechazado
``:code
Si tienes una cuenta válida de Authorize.Net deberías reemplazar el sandbox ``login`` y ``transkey`` con estos de tu cuenta, configurar ``testmode=False`` para ejecutar en la plataforma real en lugar del sandbox, y usar la información de la tarjeta de crédito dada por el visitante.
Si ``process`` devuelve ``True``, el dinero ha sido transferido desde la tarjeta de crédito del visitante a tu cuenta en Authorize.Net. ``invoice`` es sólo un string que puede ser configurado y será almacenado por Authorize.Net con esta transacción. Luego, tienes que conciliar la data con la información en tu aplicación.
Aquí un ejemplo más complejo del workflow donde se exponen más variables:
``
from gluon.contrib.AuthorizeNet import AIM
payment = AIM(login='cnpdev4289',
transkey='SR2P8g4jdEn7vFLQ',
testmod=True)
payment.setTransaction(creditcard, expiration, total, cvv, tax, invoice)
payment.setParameter('x_duplicate_window', 180) # duplicate_window de tres minutos
payment.setParameter('x_cust_id', '1324') # ID del cliente
payment.setParameter('x_first_name', 'Agent')
payment.setParameter('x_last_name', 'Smith')
payment.setParameter('x_company', 'Test Company')
payment.setParameter('x_address', '1234 Main Street')
payment.setParameter('x_city', 'Townsville')
payment.setParameter('x_state', 'NJ')
payment.setParameter('x_zip', '12345')
payment.setParameter('x_country', 'US')
payment.setParameter('x_phone', '800-555-1234')
payment.setParameter('x_description', 'Test Transaction')
payment.setParameter('x_customer_ip', socket.gethostbyname(socket.gethostname()))
payment.setParameter('x_email', 'you@example.com')
payment.setParameter('x_email_customer', False)
payment.process()
if payment.isApproved():
print 'Response Code: ', payment.response.ResponseCode
print 'Response Text: ', payment.response.ResponseText
print 'Response: ', payment.getResultResponseFull()
print 'Transaction ID: ', payment.response.TransactionID
print 'CVV Result: ', payment.response.CVVResponse
print 'Approval Code: ', payment.response.AuthCode
print 'AVS Result: ', payment.response.AVSResponse
elif payment.isDeclined():
print 'Su tarjeta de crédito fue rechazada por el banco'
elif payment.isError():
print 'No ha funcionado'
print 'aprovada',payment.isApproved()
print 'rechazada',payment.isDeclined()
print 'error',payment.isError()
``:code
Nota el código de arriba usa una cuenta de pruebas. Necesitas registrarte con Authorize.Net (no es un servicio gratuito) y provee tu propio login, transkey, testmode=True or False para el constructor AIM.
### Dropbox API
``Dropbox.com``:inxx
Dropbox es un servicio de almacenamiento muy popular. No es sólo de almacenamiento de tus archivos porque mantiene el almacenamiento en la nube sincronizado con todas tus máquinas (PCs or dispositivos). Esto permite crear grupos y dar permisos de lectura/escritura a varias carpetas hasta usuarios individuales o grupos. También mantiene historial de versiones de todos tus archivos. E incluye una carpeta llamada "Public" y cada archivo que coloques ahí tendrá su propia URL pública. Dropbox es una excelente manera de colaborar.
Puedes accedes a dropbox fácilmente registrandote en
``
https://www.dropbox.com/developers
``
Y obtendrás una ``APP_KEY`` y un ``APP_SECRET``. Una vez que tu tengas esto puedes usar Dropbox para autenticar a tus usuarios.
Crea un archivo llamado "tuapp/private/dropbox.key" y en él escribe
``
<APP_KEY>:<APP_SECERT>:app_folder
``
donde ``<APP_KEY>`` y ``APP_SECRET`` son tu llave (app_key) y la llave secreta (app_secret).
Entonces en "models/db.py" coloca:
``
from gluon.contrib.login_methods.dropbox_account import use_dropbox
use_janrain(auth,filename='private/dropbox.key')
mydropbox = auth.settings.login_form
``
Esto permite a los usuarios autenticarse en tu aplicación usando sus credenciales dropbox, y tu programa será capaz de subir archivos en sus cuentas dropbox:
``
stream = open('archivolocal.txt','rb')
mydropbox.put('archivodestino.txt',stream)
``
descargando archivos:
``
stream = mydropbox.get('archivodestino.txt')
open('archivolocal.txt','wb').write(read)
``
y obtener el listado del directorio:
``
contenidos = mydropbox.dir(path = '/')['contents']
``
### Twitter API
Aquí mostramos algunos ejemplos rápidos de cómo enviar y obtener tweets. No se requieren librerías de terceros, ya que Twitter ha creado una APIs RESTful.
Aquí tenemos un ejemplo de cómo enviar un tweet:
``
def post_tweet(usuario, clave, mensaje):
import urllib, urllib2, base64
import gluon.contrib.simplejson as sj
args= urllib.urlencode([('status', ,mensaje)])
headers={}
headers['Authorization'] = 'Basic '+base64.b64encode(
usuario+':'+ clave)
req = urllib2.Request(
'http://twitter.com/statuses/update.json',
args, headers)
return sj.loads(urllib2.urlopen(req).read())
``:code
Aquí un ejemplo de cómo recibir tweets:
``
def get_tweets():
usuario='web2py'
import urllib
import gluon.contrib.simplejson as sj
page = urllib.urlopen('http://twitter.com/%s?format=json' % usuario).read()
tweets=XML(sj.loads(page)['#timeline'])
return dict(tweets=tweets)
``:code
Para operaciones más complejas, revise la documentación de la API de Twitter.
### Streaming de archivos virtuales
``streaming``:inxx
Es común en programadores maliciosos el escaneado de sitios web búscando vulnerabilidades. Ellos usan escáneres de seguridad como Nessus para explorar el sitio web de interés, en búsqueda de scripts que son conocidos por tener vulnerabilidades. Un análisis de los logs del servidor web desde una máquina escaneadora o directamente en la base de datos Nessus revela que la mayoría de las vulnerabilidades conocidas están en scripts de PHP y ASP. Desde que estamos ejecutando web2py, no tenemos estas vulnerabilidades, pero somos escaneados por ellos. Esto es molesto, entonces nos gustaría responder a estos escáneres de vulnerabilidades y hacer al atacante entender que está desperdiciando su tiempo.
Una posibilidad es redireccionar todas las peticiones para .php, .asp, y toda solicitud sospechosa a una acción ficticia o ''dummy'' que responderá al atacante manteniendolo ocupado por una gran cantidad de tiempo. Eventualmente el atacante se rendirá y no nos escaneará de nuevo.
Esta receta requiere de dos partes.
Una aplicación dedicada llamada **atascador** con un controlador "default.py" como sigue:
``
class Atascador():
def read(self,n): return 'x'*n
def atascar(): return response.stream(Atascador(),40000)
``:code
Cuando esta acción es invocada, responderá con un infinito flujo de datos de "x"-es. 40000 caracteres al mismo tiempo.
El segundo ingrediente es un archivo "route.py" que redireccione cualquier petición terminada en .php, .asp, etc. (ambos en minúsculas y mayúsculas) a su controlador.
``
route_in=(
('.*\.(php|PHP|asp|ASP|jsp|JSP)','atascador/default/atascar'),
)
``:code
La primera vez que has sido atacado puede ocurrir una pequeña sobreutilización de recursos, pero nuestra experiencia indica que el mismo atacante no lo intentará dos veces.