mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-08-05 16:58:59 +00:00
Paypal refunds are now working. Need to add partial balances when upgrading tiers on paypal. Eventually need to think about refunds for switching providers.
This commit is contained in:
parent
0a2a9607f0
commit
01ad0e8656
6 changed files with 94 additions and 49 deletions
14
Makefile
14
Makefile
|
@ -84,10 +84,20 @@ keys:
|
|||
- openssl dhparam -out config/certificates/dhparam-2048.pem 2048
|
||||
- openssl req -x509 -nodes -new -sha256 -days 1024 -newkey rsa:2048 -keyout config/certificates/RootCA.key -out config/certificates/RootCA.pem -subj "/C=US/CN=Example-Root-CA"
|
||||
- openssl x509 -outform pem -in config/certificates/RootCA.pem -out config/certificates/RootCA.crt
|
||||
- openssl req -new -nodes -newkey rsa:2048 -keyout config/certificates/localhost.key -out config/certificates/localhost.csr -subj "/C=US/ST=YourState/L=YourCity/O=Example-Certificates/CN=localhost.local"
|
||||
- openssl req -new -nodes -newkey rsa:2048 -keyout config/certificates/localhost.key -out config/certificates/localhost.csr -subj "/C=US/ST=YourState/L=YourCity/O=Example-Certificates/CN=localhost"
|
||||
- openssl x509 -req -sha256 -days 1024 -in config/certificates/localhost.csr -CA config/certificates/RootCA.pem -CAkey config/certificates/RootCA.key -CAcreateserial -out config/certificates/localhost.crt
|
||||
- cat config/certificates/localhost.crt config/certificates/localhost.key > config/certificates/localhost.pem
|
||||
- /usr/bin/security add-trusted-cert -d -r trustAsRoot -k /Library/Keychains/System.keychain ./config/certificates/RootCA.crt
|
||||
- sudo /usr/bin/security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ./config/certificates/RootCA.crt
|
||||
|
||||
# Doesn't work yet
|
||||
mkcert:
|
||||
- mkdir config/mkcert
|
||||
- docker run -v $(shell pwd)/config/mkcert:/root/.local/share/mkcert brunopadz/mkcert-docker:latest \
|
||||
/bin/sh -c "mkcert -install && \
|
||||
mkcert -cert-file /root/.local/share/mkcert/mkcert.pem \
|
||||
-key-file /root/.local/share/mkcert/mkcert.key localhost"
|
||||
- cat config/mkcert/rootCA.pem config/mkcert/rootCA-key.pem > config/certificates/localhost.pem
|
||||
- sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ./config/mkcert/rootCA.pem
|
||||
|
||||
# Digital Ocean / Terraform
|
||||
list:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
- name: DEPLOY -> www
|
||||
hosts: www,staging
|
||||
hosts: haproxy,staging
|
||||
gather_facts: false
|
||||
vars_files:
|
||||
- ../env_vars/base.yml
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"service": {
|
||||
"name": "db-mongo-staging",
|
||||
"name": "db-mongo",
|
||||
"id": "{{ inventory_hostname }}",
|
||||
"tags": [
|
||||
"db"
|
||||
|
|
|
@ -600,51 +600,84 @@ class Profile(models.Model):
|
|||
|
||||
return ','.join(failed)
|
||||
|
||||
def refund_premium(self, partial=False):
|
||||
def refund_premium(self, partial=False, provider=None):
|
||||
refunded = False
|
||||
if provider == "paypal":
|
||||
refunded = self.refund_latest_paypal_payment(partial=partial)
|
||||
elif provider == "stripe":
|
||||
refunded = self.refund_latest_stripe_payment(partial=partial)
|
||||
else:
|
||||
# Find last payment, refund that
|
||||
payment_history = PaymentHistory.objects.filter(user=self.user,
|
||||
payment_provider__in=['paypal', 'stripe'])
|
||||
if payment_history.count():
|
||||
provider = payment_history[0].payment_provider
|
||||
if provider == "stripe":
|
||||
refunded = self.refund_latest_stripe_payment(partial=partial)
|
||||
elif provider == "paypal":
|
||||
refunded = self.refund_latest_paypal_payment(partial=partial)
|
||||
|
||||
return refunded
|
||||
|
||||
def refund_latest_stripe_payment(self, partial=False):
|
||||
refunded = False
|
||||
if not self.stripe_id:
|
||||
return
|
||||
|
||||
stripe.api_key = settings.STRIPE_SECRET
|
||||
stripe_customer = stripe.Customer.retrieve(self.stripe_id)
|
||||
stripe_payments = stripe.Charge.list(customer=stripe_customer.id).data
|
||||
if partial:
|
||||
stripe_payments[0].refund(amount=1200)
|
||||
refunded = 12
|
||||
else:
|
||||
stripe_payments[0].refund()
|
||||
self.cancel_premium_stripe()
|
||||
refunded = stripe_payments[0].amount/100
|
||||
|
||||
logging.user(self.user, "~FRRefunding stripe payment: $%s" % refunded)
|
||||
return refunded
|
||||
|
||||
def refund_latest_paypal_payment(self, partial=False):
|
||||
if not self.paypal_sub_id:
|
||||
return
|
||||
|
||||
paypal_api = self.paypal_api()
|
||||
refunded = False
|
||||
|
||||
if self.stripe_id:
|
||||
stripe.api_key = settings.STRIPE_SECRET
|
||||
stripe_customer = stripe.Customer.retrieve(self.stripe_id)
|
||||
stripe_payments = stripe.Charge.list(customer=stripe_customer.id).data
|
||||
if partial:
|
||||
stripe_payments[0].refund(amount=1200)
|
||||
refunded = 12
|
||||
else:
|
||||
stripe_payments[0].refund()
|
||||
self.cancel_premium()
|
||||
refunded = stripe_payments[0].amount/100
|
||||
logging.user(self.user, "~FRRefunding stripe payment: $%s" % refunded)
|
||||
# Find transaction from subscription
|
||||
now = datetime.datetime.now()
|
||||
# 200 days captures Paypal's 180 day limit on refunds
|
||||
start_date = (now-datetime.timedelta(days=200)).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
end_date = now.strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
try:
|
||||
transactions = paypal_api.get(f"/v1/billing/subscriptions/{self.paypal_sub_id}/transactions?start_time={start_date}&end_time={end_date}")
|
||||
except paypalrestsdk.ResourceNotFound:
|
||||
transactions = {}
|
||||
if 'transactions' not in transactions or not len(transactions['transactions']):
|
||||
logging.user(self.user, f"~FRCouldn't find paypal transactions: {self.paypal_sub_id} {transactions}")
|
||||
return
|
||||
|
||||
# Refund the latest transaction
|
||||
transaction = transactions['transactions'][0]
|
||||
today = datetime.datetime.now().strftime('%B %d, %Y')
|
||||
url = f"/v2/payments/captures/{transaction['id']}/refund"
|
||||
try:
|
||||
response = paypal_api.post(url, {
|
||||
'reason': f"Refunded on {today}"
|
||||
})
|
||||
except paypalrestsdk.exceptions.ResourceInvalid as e:
|
||||
response = e.response.json()
|
||||
if len(response.get('details', [])):
|
||||
response = response['details'][0]['description']
|
||||
if 'status' in response and response['status'] == "COMPLETED":
|
||||
refunded = int(float(transaction['amount_with_breakdown']['gross_amount']['value']))
|
||||
logging.user(self.user, "~FRRefunding paypal payment: $%s" % refunded)
|
||||
else:
|
||||
self.cancel_premium()
|
||||
|
||||
paypal_opts = {
|
||||
'API_ENVIRONMENT': 'SANDBOX',
|
||||
'API_USERNAME': settings.PAYPAL_API_USERNAME,
|
||||
'API_PASSWORD': settings.PAYPAL_API_PASSWORD,
|
||||
'API_SIGNATURE': settings.PAYPAL_API_SIGNATURE,
|
||||
'API_CA_CERTS': False,
|
||||
}
|
||||
paypal = PayPalInterface(**paypal_opts)
|
||||
transactions = PayPalIPN.objects.filter(custom=self.user.username,
|
||||
txn_type='subscr_payment'
|
||||
).order_by('-payment_date')
|
||||
if not transactions:
|
||||
transactions = PayPalIPN.objects.filter(payer_email=self.user.email,
|
||||
txn_type='subscr_payment'
|
||||
).order_by('-payment_date')
|
||||
if transactions:
|
||||
transaction = transactions[0]
|
||||
refund = paypal.refund_transaction(transaction.txn_id)
|
||||
try:
|
||||
refunded = int(float(refund.raw['TOTALREFUNDEDAMOUNT'][0]))
|
||||
except KeyError:
|
||||
refunded = int(transaction.payment_gross)
|
||||
logging.user(self.user, "~FRRefunding paypal payment: $%s" % refunded)
|
||||
else:
|
||||
logging.user(self.user, "~FRCouldn't refund paypal payment: not found by username or email")
|
||||
refunded = 0
|
||||
logging.user(self.user, "~FRCouldn't refund paypal payment: %s" % response)
|
||||
refunded = response
|
||||
|
||||
self.cancel_premium_paypal()
|
||||
|
||||
return refunded
|
||||
|
||||
|
@ -680,7 +713,7 @@ class Profile(models.Model):
|
|||
response = paypal_api.post(url, {
|
||||
'reason': f"Cancelled on {today}"
|
||||
})
|
||||
logging.user(self.user, f"response: {response}")
|
||||
# logging.user(self.user, f"response: {response}")
|
||||
|
||||
logging.user(self.user, "~FRCanceling Paypal subscription: %s" % paypal_id)
|
||||
return True
|
||||
|
|
|
@ -645,15 +645,16 @@ def cancel_premium(request):
|
|||
def refund_premium(request):
|
||||
user_id = request.POST.get('user_id')
|
||||
partial = request.POST.get('partial', False)
|
||||
provider = request.POST.get('provider', None)
|
||||
user = User.objects.get(pk=user_id)
|
||||
try:
|
||||
refunded = user.profile.refund_premium(partial=partial)
|
||||
refunded = user.profile.refund_premium(partial=partial, provider=provider)
|
||||
except stripe.error.InvalidRequestError as e:
|
||||
refunded = e
|
||||
except PayPalAPIResponseError as e:
|
||||
refunded = e
|
||||
|
||||
return {'code': 1 if refunded else -1, 'refunded': refunded}
|
||||
return {'code': 1 if type(refunded) == int else -1, 'refunded': refunded}
|
||||
|
||||
@staff_member_required
|
||||
@ajax_login_required
|
||||
|
|
|
@ -7099,6 +7099,7 @@ form.opml_import_form input {
|
|||
.NB-module-account-subscription .NB-module-stats-count-description {
|
||||
color: #0D003C;
|
||||
margin-bottom: 4px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.NB-module-stats-count-graph {
|
||||
|
|
Loading…
Add table
Reference in a new issue