CCrypto VPN public website https://vpn.ccrypto.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

233 lines
7.6 KiB

  1. import json
  2. from django.utils.translation import ugettext_lazy as _
  3. from django.core.urlresolvers import reverse
  4. from .base import BackendBase
  5. class StripeBackend(BackendBase):
  6. backend_id = 'stripe'
  7. backend_verbose_name = _("Stripe")
  8. backend_display_name = _("Credit Card")
  9. backend_has_recurring = True
  10. def get_plan_id(self, period):
  11. return 'ccvpn_' + period
  12. def __init__(self, settings):
  13. if 'API_KEY' not in settings or 'PUBLIC_KEY' not in settings:
  14. return
  15. import stripe
  16. self.stripe = stripe
  17. stripe.api_key = settings['API_KEY']
  18. self.pubkey = settings['PUBLIC_KEY']
  19. self.header_image = settings.get('HEADER_IMAGE', '')
  20. self.currency = settings.get('CURRENCY', 'EUR')
  21. self.name = settings.get('NAME', 'VPN Payment')
  22. self.backend_enabled = True
  23. def new_payment(self, payment):
  24. desc = str(payment.time) + ' for ' + payment.user.username
  25. form = '''
  26. <form action="{post}" method="POST">
  27. <script
  28. src="https://checkout.stripe.com/checkout.js" class="stripe-button"
  29. data-key="{pubkey}"
  30. data-image="{img}"
  31. data-name="{name}"
  32. data-currency="{curr}"
  33. data-description="{desc}"
  34. data-amount="{amount}"
  35. data-email="{email}"
  36. data-locale="auto"
  37. data-zip-code="true"
  38. data-alipay="true">
  39. </script>
  40. </form>
  41. '''
  42. return form.format(
  43. post=reverse('payments:cb_stripe', args=(payment.id,)),
  44. pubkey=self.pubkey,
  45. img=self.header_image,
  46. email=payment.user.email or '',
  47. name=self.name,
  48. desc=desc,
  49. amount=payment.amount,
  50. curr=self.currency,
  51. )
  52. def new_subscription(self, subscr):
  53. desc = 'Subscription (' + str(subscr.period) + ') for ' + subscr.user.username
  54. form = '''
  55. <form action="{post}" method="POST">
  56. <script
  57. src="https://checkout.stripe.com/checkout.js" class="stripe-button"
  58. data-key="{pubkey}"
  59. data-image="{img}"
  60. data-name="{name}"
  61. data-currency="{curr}"
  62. data-description="{desc}"
  63. data-amount="{amount}"
  64. data-email="{email}"
  65. data-locale="auto"
  66. data-zip-code="true"
  67. data-alipay="true">
  68. </script>
  69. </form>
  70. <noscript><p>Please enable JavaScript to use the payment form.</p></noscript>
  71. '''
  72. return form.format(
  73. post=reverse('payments:cb_stripe_subscr', args=(subscr.id,)),
  74. pubkey=self.pubkey,
  75. img=self.header_image,
  76. email=subscr.user.email or '',
  77. name=self.name,
  78. desc=desc,
  79. amount=subscr.period_amount,
  80. curr=self.currency,
  81. )
  82. def cancel_subscription(self, subscr):
  83. if subscr.status not in ('new', 'unconfirmed', 'active'):
  84. return
  85. try:
  86. cust = self.stripe.Customer.retrieve(subscr.backend_extid)
  87. except self.stripe.error.InvalidRequestError:
  88. return
  89. try:
  90. # Delete customer and cancel any active subscription
  91. cust.delete()
  92. except self.stripe.error.InvalidRequestError:
  93. pass
  94. subscr.status = 'cancelled'
  95. subscr.save()
  96. def callback(self, payment, request):
  97. post_data = request.POST
  98. token = post_data.get('stripeToken')
  99. if not token:
  100. payment.status = 'cancelled'
  101. payment.status_message = _("No payment information was received.")
  102. return
  103. months = int(payment.time.days / 30)
  104. username = payment.user.username
  105. try:
  106. charge = self.stripe.Charge.create(
  107. amount=payment.amount,
  108. currency=self.currency,
  109. card=token,
  110. description="%d months for %s" % (months, username),
  111. )
  112. payment.backend_extid = charge['id']
  113. if charge['refunded'] or not charge['paid']:
  114. payment.status = 'rejected'
  115. payment.status_message = _("The payment has been refunded or rejected.")
  116. payment.save()
  117. return
  118. payment.paid_amount = int(charge['amount'])
  119. if payment.paid_amount < payment.amount:
  120. payment.status = 'error'
  121. payment.status_message = _("The paid amount is under the required amount.")
  122. payment.save()
  123. return
  124. payment.status = 'confirmed'
  125. payment.status_message = None
  126. payment.save()
  127. payment.user.vpnuser.add_paid_time(payment.time)
  128. payment.user.vpnuser.on_payment_confirmed(payment)
  129. payment.user.vpnuser.save()
  130. except self.stripe.error.CardError as e:
  131. payment.status = 'rejected'
  132. payment.status_message = e.json_body['error']['message']
  133. payment.save()
  134. def callback_subscr(self, subscr, request):
  135. post_data = request.POST
  136. token = post_data.get('stripeToken')
  137. if not token:
  138. subscr.status = 'cancelled'
  139. subscr.save()
  140. return
  141. try:
  142. cust = self.stripe.Customer.create(
  143. source=token,
  144. plan=self.get_plan_id(subscr.period),
  145. )
  146. except self.stripe.error.InvalidRequestError:
  147. return
  148. except self.stripe.CardError as e:
  149. subscr.status = 'error'
  150. subscr.backend_data['stripe_error'] = e.json_body['error']['message']
  151. return
  152. # We don't know much about the new Payment, but we know it
  153. # succeeded. Wekhooks aren't very reliable, so let's mark it as active
  154. # anyway.
  155. subscr.status = 'active'
  156. subscr.backend_extid = cust['id']
  157. subscr.save()
  158. def webhook_payment_succeeded(self, event):
  159. from payments.models import Subscription, Payment
  160. invoice = event['data']['object']
  161. customer_id = invoice['customer']
  162. # Prevent making duplicate Payments if event is received twice
  163. pc = Payment.objects.filter(backend_extid=invoice['id']).count()
  164. if pc > 0:
  165. return
  166. subscr = Subscription.objects.get(backend_extid=customer_id)
  167. payment = subscr.create_payment()
  168. payment.status = 'confirmed'
  169. payment.paid_amount = invoice['total']
  170. payment.backend_extid = invoice['id']
  171. payment.backend_data = {'event_id': event['id']}
  172. payment.save()
  173. payment.user.vpnuser.add_paid_time(payment.time)
  174. payment.user.vpnuser.on_payment_confirmed(payment)
  175. payment.user.vpnuser.save()
  176. payment.save()
  177. subscr.status = 'active'
  178. subscr.save()
  179. def webhook(self, request):
  180. try:
  181. event_json = json.loads(request.body.decode('utf-8'))
  182. event = self.stripe.Event.retrieve(event_json["id"])
  183. except (ValueError, self.stripe.error.InvalidRequestError):
  184. return False
  185. if event['type'] == 'invoice.payment_succeeded':
  186. self.webhook_payment_succeeded(event)
  187. return True
  188. def get_ext_url(self, payment):
  189. if not payment.backend_extid:
  190. return None
  191. return 'https://dashboard.stripe.com/payments/%s' % payment.backend_extid
  192. def get_subscr_ext_url(self, subscr):
  193. if not subscr.backend_extid:
  194. return None
  195. return 'https://dashboard.stripe.com/customers/%s' % subscr.backend_extid