def charge(request): """Endpoint for creating a charge based on a token""" token_form = SimpleInvoicePaymentForm(request.POST) if not token_form.is_valid(): raise exceptions.GatewayException(token_form.errors) invoice_id = token_form.cleaned_data['invoice'] charge_token = token_form.cleaned_data['token'] try: inv = Invoice.objects.get(pk=invoice_id, client__in=request.user.clients.all()) except Invoice.DoesNotExist: raise exceptions.InvoicePaymentException( 'Invoice {} does not exist'.format(invoice_id), invoice_id=invoice_id) if inv.balance <= 0: raise exceptions.InvoicePaymentException( 'Invoice {} is already paid'.format(invoice_id), invoice_id=invoice_id) stripe.api_key = conf.secret_key token_id = charge_token.get('id') if token_id is None: raise exceptions.InvoicePaymentException('Charge failed', invoice_id=invoice_id) try: charge_result = stripe.Charge.create( amount=utils.convert_amount_to_api(inv.balance, inv.currency.pk), currency=inv.currency.pk.lower(), description=_('Invoice {} payment').format(inv.pk), metadata={"invoice": inv.pk}, source=token_id, idempotency_key=None) except CardError as e: LOG.exception(e) body = e.json_body err = body.get('error', {}) raise exceptions.InvoicePaymentException(err.get( 'message', 'Charge failed'), invoice_id=invoice_id) except StripeError as e: LOG.exception(e) raise exceptions.InvoicePaymentException('Charge failed', invoice_id=invoice_id) process_charge(charge=charge_result) relative_url = 'billing/invoices/{}'.format(invoice_id) return HttpResponseRedirect( fleio_join_url(settings.FRONTEND_URL, relative_url))
def pay_invoice(request): invoice_id = request.query_params.get('invoice') if invoice_id is None: raise exceptions.GatewayException("An 'invoice' parameter is required") try: inv = Invoice.objects.get(pk=invoice_id, client__in=request.user.clients.all()) except Invoice.DoesNotExist: raise exceptions.GatewayException( 'Invoice {} does not exist'.format(invoice_id)) if inv.balance <= 0: raise exceptions.InvoicePaymentException( 'Invoice {} is already paid'.format(invoice_id), invoice_id=invoice_id) amount = str(inv.balance) pay_message = _('Pay {} {} for invoice {}'.format(amount, inv.currency.pk, inv.pk)) recurring = request.query_params.get('recurring', False) recurring = True if (recurring == 'True' or recurring == 'true' or recurring == '1') else False if recurring: existing_recurrent_payments_config = RecurringPayments.objects.filter( client=inv.client).first() if existing_recurrent_payments_config: raise exceptions.GatewayException( _('Recurrent payments were already configured for stripe.')) token_form = SetupRecurringPaymentsForm(initial={'invoice': inv.pk}) stripe.api_key = conf.secret_key payment_intent = stripe.PaymentIntent.create( amount=utils.convert_amount_to_api(inv.balance, inv.currency.pk), currency=inv.currency.pk.lower(), setup_future_usage='off_session', metadata={"invoice": inv.pk}, ) return render(request=request, template_name='stripe/setup_recurring.html', context={ 'payment_vars': json.dumps({'publicKey': conf.public_key}), 'pay_message': pay_message, 'client_secret': payment_intent.client_secret, 'token_form': token_form }) token_form = SimpleInvoicePaymentForm(initial={'invoice': inv.pk}) return render(request=request, template_name='stripe/pay_invoice.html', context={ 'payment_vars': json.dumps({'publicKey': conf.public_key}), 'pay_message': pay_message, 'token_form': token_form })
def pay_invoice(request): invoice_id = request.query_params.get('invoice') try: Invoice.objects.get(pk=invoice_id, client__in=request.user.clients.all()) # type: Invoice except Invoice.DoesNotExist: raise gateway_exceptions.GatewayException(_('Invoice {} does not exist').format(invoice_id)) try: configure() payment = create_payment(invoice_id) except Exception as create_payment_exception: raise gateway_exceptions.InvoicePaymentException(message=str(create_payment_exception), invoice_id=invoice_id) return HttpResponseRedirect(get_approval_url(payment))
def pay_invoice(request): invoice_id = request.query_params.get('invoice') try: process_test_payment(test_mode=conf.test_mode) except Exception as create_payment_exception: # handle possible exception raise gateway_exceptions.InvoicePaymentException( message=str(create_payment_exception), invoice_id=invoice_id) # redirect to invoice relative_url = 'billing/invoices/{}'.format(invoice_id) return HttpResponseRedirect( fleio_join_url(settings.FRONTEND_URL, relative_url))
def pay_invoice(request): invoice_id = request.query_params.get('invoice') try: Invoice.objects.get( pk=invoice_id, client__in=request.user.clients.all()) # type: Invoice except Invoice.DoesNotExist: raise gateway_exceptions.GatewayException( _('Invoice {} does not exist').format(invoice_id)) try: payu_client = PayUClient(invoice_id=invoice_id, request=request) return payu_client.create_order() except Exception as create_payment_exception: raise gateway_exceptions.InvoicePaymentException( message=str(create_payment_exception), invoice_id=invoice_id)
def process_charge(charge): """Process a charge result""" gateway = Gateway.objects.get(name='stripe') external_id = charge.id transaction_id = None transaction_status = charge_status(charge) invoice_id = charge.metadata.get('invoice') if external_id is not None: try: existing_transaction = Transaction.objects.get( external_id=external_id, gateway=gateway) transaction_id = existing_transaction.id except Transaction.DoesNotExist: real_amount = utils.convert_amount_from_api( charge.amount, charge.currency) serializer_data = { 'invoice': invoice_id, 'external_id': external_id, 'amount': real_amount, 'currency': charge.currency.upper(), 'gateway': gateway.pk, 'fee': gateway.get_fee(amount=real_amount), 'date_initiated': datetime.datetime.fromtimestamp(charge.created), 'extra': {}, 'status': transaction_status } tr_ser = AddTransactionSerializer(data=serializer_data) if tr_ser.is_valid(raise_exception=False): new_transaction = tr_ser.save() transaction_id = new_transaction.id else: LOG.error('Could not process charge for stripe: {}'.format( json.dumps(tr_ser.errors))) raise exceptions.InvoicePaymentException( 'Could not process charge for invoice {}.', invoice_id=invoice_id) else: # Update the transaction status only existing_transaction.status = transaction_status existing_transaction.save(update_fields=['status']) gateway.log_callback(external_id=external_id, status=charge.status, data=charge, error=(charge.status == 'failed'), error_code=charge.failure_code, error_info=charge.failure_message) if transaction_status == TransactionStatus.FAILURE: raise exceptions.GatewayException(charge.failure_message or 'error') else: if transaction_status == TransactionStatus.SUCCESS: activity_helper.start_generic_activity( category_name='stripe', activity_class='stripe payment', invoice_id=invoice_id) invoice_add_payment.delay(invoice_id=invoice_id, amount=utils.convert_amount_from_api( charge.amount, charge.currency), currency_code=charge.currency.upper(), transaction_id=transaction_id) activity_helper.end_activity()
def process_refund(refund): # FIXME(tomo): This does not support partial refunds # Partial refunds should be allowed if the invoice balance is higher than the requested amount # Right now, creating a partial refund will set the transaction to refunded even if you refund # less than the payment value. gateway = Gateway.objects.get(name='stripe') external_id = refund.id existing_transaction_id = None invoice_id = None if refund.status == 'succeeded': if external_id is not None: try: existing_transaction = Transaction.objects.get( external_id=refund.charge, gateway=gateway) existing_transaction_id = existing_transaction.pk invoice_id = existing_transaction.invoice_id except Transaction.DoesNotExist: raise exceptions.GatewayException('Transaction does not exist') else: existing_transaction.status = TransactionStatus.REFUNDED existing_transaction.save(update_fields=['status']) gateway.log_callback(external_id=external_id, status=refund.status, data=refund, error=False) # Create a new refund transaction real_amount = utils.convert_amount_from_api(refund.amount, refund.currency) created_date = datetime.datetime.fromtimestamp(refund.created) new_transaction_status = TransactionStatus.REFUNDED new_refund_transaction = { 'invoice': invoice_id, 'external_id': external_id, 'amount': real_amount, 'currency': refund.currency, 'gateway': gateway.pk, 'fee': gateway.get_fee(amount=decimal.Decimal(real_amount)), 'date_initiated': created_date, 'extra': { 'balance_transaction': refund.balance_transaction }, 'refunded_transaction': existing_transaction_id, 'status': new_transaction_status } tr_ser = AddTransactionSerializer(data=new_refund_transaction) if tr_ser.is_valid(raise_exception=False): new_transaction = tr_ser.save() new_transaction_id = new_transaction.id activity_helper.start_generic_activity( category_name='stripe', activity_class='stripe payment refund', invoice_id=invoice_id) invoice_refund_payment.delay( transaction_id=new_transaction.refunded_transaction.pk, amount=utils.convert_amount_from_api(refund.amount, refund.currency), to_client_credit=False, new_transaction_id=new_transaction_id) activity_helper.end_activity() else: LOG.error(tr_ser.errors) raise exceptions.InvoicePaymentException( 'Unable to process refund', invoice_id=invoice_id) elif refund.status == 'failed': gateway.log_callback(external_id=external_id, status=refund.status, data=refund, error=(refund.status == 'failed'), error_info=refund.failure_reason) raise exceptions.GatewayException( _('Refund for transaction {} failed').format(external_id))
def process_response(resp): gateway = Gateway.objects.get(name='romcard') external_id = resp.get('external_id') transaction_status = resp.get('transaction_status') transaction_error = resp.get('error', False) invoice = invoice_id_from_api(resp.get('invoice')) transaction_id = None if external_id is not None and not transaction_error: # Check for duplicate transaction based on external ID and NONCE possible_duplicates = Transaction.objects.filter( external_id=external_id, gateway=gateway) for pdup in possible_duplicates: if (pdup.extra.get('RRN') == resp.get('transaction_extra', {}).get('RRN') and pdup.extra.get('NONCE') == resp.get( 'transaction_extra', {}).get('NONCE')): # Dupliocate transaction LOG.warning('Romcard duplicate transaction {} ignored'.format( external_id)) return HttpResponseRedirect( fleio_join_url(settings.FRONTEND_URL, 'billing/invoices')) existing_transaction = Transaction.objects.filter( external_id=external_id, gateway=gateway, refunded_transaction__isnull=True).first() if existing_transaction: transaction_id = existing_transaction.pk # Update the transaction status only existing_transaction.status = transaction_status existing_transaction.save(update_fields=['status']) else: serializer_data = { 'invoice': invoice, 'external_id': external_id, 'amount': resp['amount'], 'currency': resp['currency'], 'gateway': gateway.pk, 'fee': gateway.get_fee(amount=decimal.Decimal(resp['amount'])), 'date_initiated': resp['transaction_date'], 'extra': resp['transaction_extra'], 'status': resp['transaction_status'] } tr_ser = AddTransactionSerializer(data=serializer_data) if tr_ser.is_valid(raise_exception=False): new_transaction = tr_ser.save() transaction_id = new_transaction.pk else: LOG.error('Romcard transaction error: {}'.format( tr_ser.errors)) raise exceptions.InvoicePaymentException('Transaction error', invoice_id=invoice) gateway.log_callback(external_id=external_id, status=resp.get('transaction_status', 'unknown'), data=resp.get('log_data'), error=transaction_error, error_code=resp.get('error_code'), error_info=resp.get('error_info')) if transaction_error: raise exceptions.InvoicePaymentException(resp.get( 'error_info', 'error'), invoice_id=invoice) else: relative_invoice_url = 'billing/invoices/{0}'.format(invoice) relative_staff_invoice_url = 'staff/billing/invoices/{0}'.format( invoice) if transaction_status == TransactionStatus.PREAUTH: activity_helper.start_generic_activity( category_name='romcard', activity_class='romcard payment', invoice_id=invoice) invoice_add_payment.delay(invoice_id=invoice, amount=resp.get('amount'), currency_code=resp.get('currency'), transaction_id=transaction_id) activity_helper.end_activity() return HttpResponseRedirect( fleio_join_url(settings.FRONTEND_URL, relative_invoice_url)) elif transaction_status == TransactionStatus.CONFIRMED: if resp.get('is_recurring', False): activity_helper.start_generic_activity( category_name='romcard', activity_class='romcard payment', invoice_id=invoice) invoice_add_payment.delay(invoice_id=invoice, amount=resp.get('amount'), currency_code=resp.get('currency'), transaction_id=transaction_id) activity_helper.end_activity() Transaction.objects.filter( external_id=external_id, refunded_transaction__isnull=True).update( status=TransactionStatus.CONFIRMED) return HttpResponseRedirect( fleio_join_url(settings.FRONTEND_URL, relative_staff_invoice_url)) elif transaction_status in (TransactionStatus.REFUNDED, TransactionStatus.PARTIAL_REFUNDED): existing_transaction = Transaction.objects.filter( external_id=external_id, refunded_transaction__isnull=True).first() if not existing_transaction: LOG.error( 'Error when processing transaction that should have RRN (external_id) set to {}' .format(external_id)) raise exceptions.InvoicePaymentException('Transaction error', invoice_id=invoice) new_refund_transaction = { 'invoice': invoice, 'external_id': external_id, 'amount': resp.get('amount'), 'currency': resp['currency'], 'gateway': gateway.pk, 'fee': gateway.get_fee(amount=decimal.Decimal(resp['amount'])), 'date_initiated': resp['transaction_date'], 'extra': resp['transaction_extra'], 'refunded_transaction': existing_transaction.pk, 'status': transaction_status } tr_ser = AddTransactionSerializer(data=new_refund_transaction) if tr_ser.is_valid(raise_exception=False): new_transaction = tr_ser.save() else: LOG.error('Romcard transaction error: {}'.format( tr_ser.errors)) raise exceptions.InvoicePaymentException('Transaction error', invoice_id=invoice) activity_helper.start_generic_activity( category_name='romcard', activity_class='romcard payment refund', invoice_id=invoice) invoice_refund_payment.delay(transaction_id=transaction_id, amount=resp.get('amount'), to_client_credit=False, new_transaction_id=new_transaction.pk) activity_helper.end_activity() return HttpResponseRedirect( fleio_join_url(settings.FRONTEND_URL, relative_staff_invoice_url)) else: LOG.warning('Romcard callback not processed: {}'.format(resp)) return HttpResponseRedirect( fleio_join_url(settings.FRONTEND_URL, 'billing/invoices'))
def confirm_payment(request): # PayPal does a full-page redirect to the return_url that was specified when the payment was created, # with PayerID and paymentId appended to the URL. payment_id = request.query_params.get('paymentId') payer_id = request.query_params.get('PayerID') configure() payment = get_payment(payment_id) invoice_id = get_invoice_id(payment) amount = get_payment_amount(payment) LOG.info('Payment %s for invoice %s processed' % (payment_id, invoice_id)) if payment.execute({'payer_id': payer_id}): sale_id = get_sale_id(payment) sale = paypalrestsdk.Sale.find(sale_id) try: existing_transaction = Transaction.objects.get(external_id=payment_id, gateway=gateway) transaction_id = existing_transaction.id except Transaction.DoesNotExist: serializer_data = {'invoice': invoice_id, 'external_id': payment_id, 'amount': amount.total, 'currency': amount.currency, 'gateway': gateway.pk, 'fee': sale.transaction_fee.value, 'date_initiated': payment.update_time, 'extra': None, 'status': TransactionStatus.SUCCESS} tr_ser = AddTransactionSerializer(data=serializer_data) if tr_ser.is_valid(raise_exception=False): new_transaction = tr_ser.save() transaction_id = new_transaction.id else: raise gateway_exceptions.InvoicePaymentException(message=tr_ser.errors, invoice_id=invoice_id) else: # Update the transaction status only existing_transaction.status = TransactionStatus.SUCCESS existing_transaction.save(update_fields=['status']) activity_helper.start_generic_activity( category_name='paypal', activity_class='paypal payment', invoice_id=invoice_id ) invoice_add_payment.delay(invoice_id=invoice_id, amount=amount.total, currency_code=amount.currency, transaction_id=transaction_id) activity_helper.end_activity() else: raise gateway_exceptions.InvoicePaymentException(message=format_error(payment), invoice_id=invoice_id) gateway.log_callback(external_id=payment_id, status=payment.state, data='', error=False, error_code='', error_info='') return HttpResponseRedirect(get_invoice_url(invoice_id))
def callback(request): if not PayUUtils.validate_open_payu_signature( open_payu_signature=request.headers.get('OpenPayu-Signature', None), body=request.body, ): raise gateway_exceptions.GatewayException( 'Did not receive or received invalid OpenPayu-Signature') gateway = Gateway.objects.get(name='payu') order_details = request.data.get('order', None) if not order_details: raise Exception('No order details') external_id = order_details.get('orderId') invoice_id = PayUUtils.get_invoice_id_from_external_order_id( external_order_id=order_details.get('extOrderId')) transaction_status = order_details.get('status') total_amount = PayUUtils.get_payu_amount_in_fleio_amount( amount=order_details.get('totalAmount')) if order_details.get('status') == PayUTransactionStatus.canceled: # process refund for pre auth transaction existing_transaction = Transaction.objects.filter( external_id=external_id, gateway=gateway, refunded_transaction__isnull=True).first() if not existing_transaction: raise gateway_exceptions.GatewayException( 'Could not process cancellation notification because there is no transaction to refund' ) new_refund_transaction = { 'invoice': invoice_id, 'external_id': external_id, 'amount': str(total_amount), 'currency': order_details.get('currencyCode'), 'gateway': gateway.pk, 'fee': gateway.get_fee(amount=decimal.Decimal(total_amount)), 'date_initiated': utcnow(), 'extra': {}, 'refunded_transaction': existing_transaction.pk, 'status': TransactionStatus.REFUNDED } transaction_serializer = AddTransactionSerializer( data=new_refund_transaction) if transaction_serializer.is_valid(raise_exception=False): new_transaction = transaction_serializer.save() activity_helper.start_generic_activity( category_name='payu', activity_class='payu payment refund', invoice_id=existing_transaction.invoice.id) invoice_refund_payment.delay( transaction_id=existing_transaction.id, amount=total_amount, to_client_credit=False, new_transaction_id=new_transaction.pk) activity_helper.end_activity() else: LOG.error('PayU transaction error: {}'.format( transaction_serializer.errors)) raise gateway_exceptions.GatewayException( 'Could not process cancellation notification because serialized data is invalid' ) return Response({'detail': 'Ok'}) # process pre-auth, confirmation notifications existing_transaction = Transaction.objects.filter( external_id=external_id, gateway=gateway, refunded_transaction__isnull=True).first() if existing_transaction: # Update the transaction status and mark invoice as paid (still needs capture if automatic capture is not # enabled in PayU) if client successfully made the payment existing_transaction.status = PayUTransactionStatus.to_transaction_model_status.get( transaction_status) existing_transaction.save(update_fields=['status']) if (transaction_status == PayUTransactionStatus.waiting_for_confirmation or (transaction_status == PayUTransactionStatus.completed and existing_transaction.invoice and existing_transaction.invoice.is_unpaid())): activity_helper.start_generic_activity( category_name='payu', activity_class='payu payment', invoice_id=invoice_id) invoice_add_payment.delay( invoice_id=invoice_id, amount=total_amount, currency_code=order_details.get('currencyCode'), transaction_id=existing_transaction.id) activity_helper.end_activity() return Response({'detail': 'Ok'}) elif transaction_status == PayUTransactionStatus.pending: serializer_data = { 'invoice': invoice_id, 'external_id': external_id, 'amount': total_amount, 'currency': order_details.get('currencyCode'), 'gateway': gateway.pk, 'fee': gateway.get_fee(amount=decimal.Decimal(total_amount)), 'date_initiated': order_details.get('orderCreateDate'), 'extra': {}, 'status': PayUTransactionStatus.to_transaction_model_status.get( transaction_status) } add_transaction_serializer = AddTransactionSerializer( data=serializer_data) if add_transaction_serializer.is_valid(raise_exception=False): add_transaction_serializer.save() else: LOG.error('PayU transaction error: {}'.format( add_transaction_serializer.errors)) raise gateway_exceptions.InvoicePaymentException( 'Transaction error', invoice_id=invoice_id) return Response({'detail': 'Ok'})
def refund(request): gateway = Gateway.objects.get(name='payu') transaction_id = request.query_params.get('transaction') transaction = Transaction.objects.get(id=transaction_id) invoice = transaction.invoice payu_client = PayUClient(invoice_id=invoice.id, request=request) if transaction.status == Transaction.TRANSACTION_STATUS.CONFIRMED: # do a refund for transaction status confirmed try: response = payu_client.refund( transaction_external_id=transaction.external_id) except Exception as e: raise gateway_exceptions.GatewayException( 'Invalid payu refund action: {}'.format(str(e))) if response.status_code == 200 or response.status_code == 201: response_text = json.loads(response.text) refund_data = response_text.get('refund', {}) new_refund_transaction = { 'invoice': invoice.id, 'external_id': transaction.external_id, 'amount': str( PayUUtils.get_payu_amount_in_fleio_amount( refund_data.get('amount'))), 'currency': refund_data.get('currencyCode'), 'gateway': gateway.pk, 'fee': gateway.get_fee(amount=decimal.Decimal(transaction.amount)), 'date_initiated': refund_data.get('creationDateTime'), 'extra': { 'refundId': refund_data.get('refundId'), 'extRefundId': refund_data.get('extRefundId'), }, 'refunded_transaction': transaction.pk, 'status': TransactionStatus.REFUNDED } transaction_serializer = AddTransactionSerializer( data=new_refund_transaction) if transaction_serializer.is_valid(raise_exception=False): new_transaction = transaction_serializer.save() activity_helper.start_generic_activity( category_name='payu', activity_class='payu payment refund', invoice_id=invoice.id) invoice_refund_payment.delay( transaction_id=transaction_id, amount=PayUUtils.get_payu_amount_in_fleio_amount( refund_data.get('amount')), to_client_credit=False, new_transaction_id=new_transaction.pk) activity_helper.end_activity() else: LOG.error('PayU transaction error: {}'.format( transaction_serializer.errors)) raise gateway_exceptions.InvoicePaymentException( 'Transaction error', invoice_id=invoice.id) return Response({'detail': 'Ok'}) else: # do a cancellation request for when transaction was not yet confirmed try: payu_client.cancel(transaction_external_id=transaction.external_id) except Exception as e: raise gateway_exceptions.GatewayException( 'Invalid payu refund action: {}'.format(str(e))) return Response({'detail': 'Ok'}) response_text = json.loads(response.text) status = response_text.get('status') status_desc = status.get('statusDesc') raise Exception('Could not make capture action. {}'.format( status_desc if status_desc else ''))