mirror of
https://github.com/neocities/neocities.git
synced 2025-05-11 17:28:29 +02:00
an unfinished start on proper paypal recurring integration
This commit is contained in:
parent
5346b7345e
commit
c4376bc580
15 changed files with 175 additions and 53 deletions
4
Gemfile
4
Gemfile
|
@ -25,7 +25,9 @@ gem 'filesize'
|
||||||
gem 'thread'
|
gem 'thread'
|
||||||
gem 'scrypt'
|
gem 'scrypt'
|
||||||
gem 'rack-cache'
|
gem 'rack-cache'
|
||||||
gem 'rest-client'
|
gem 'rest-client', require: 'rest_client'
|
||||||
|
gem 'addressable'
|
||||||
|
gem 'paypal-recurring', require: 'paypal/recurring'
|
||||||
|
|
||||||
platform :mri, :rbx do
|
platform :mri, :rbx do
|
||||||
gem 'magic' # sudo apt-get install file, For OSX: brew install libmagic
|
gem 'magic' # sudo apt-get install file, For OSX: brew install libmagic
|
||||||
|
|
|
@ -111,6 +111,7 @@ GEM
|
||||||
netrc (0.10.3)
|
netrc (0.10.3)
|
||||||
nokogiri (1.6.3.1)
|
nokogiri (1.6.3.1)
|
||||||
mini_portile (= 0.6.0)
|
mini_portile (= 0.6.0)
|
||||||
|
paypal-recurring (1.1.0)
|
||||||
pg (0.17.1)
|
pg (0.17.1)
|
||||||
phantomjs (1.9.7.1)
|
phantomjs (1.9.7.1)
|
||||||
poltergeist (1.5.1)
|
poltergeist (1.5.1)
|
||||||
|
@ -233,6 +234,7 @@ PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
addressable
|
||||||
ago
|
ago
|
||||||
bcrypt
|
bcrypt
|
||||||
capybara_minitest_spec
|
capybara_minitest_spec
|
||||||
|
@ -252,6 +254,7 @@ DEPENDENCIES
|
||||||
minitest
|
minitest
|
||||||
minitest-reporters
|
minitest-reporters
|
||||||
mocha
|
mocha
|
||||||
|
paypal-recurring
|
||||||
pg
|
pg
|
||||||
poltergeist
|
poltergeist
|
||||||
pry
|
pry
|
||||||
|
|
2
app.rb
2
app.rb
|
@ -27,7 +27,7 @@ before do
|
||||||
if request.path.match /^\/api\//i
|
if request.path.match /^\/api\//i
|
||||||
@api = true
|
@api = true
|
||||||
content_type :json
|
content_type :json
|
||||||
elsif request.path.match /^\/stripe_webhook$/
|
elsif request.path.match /^\/webhooks\//
|
||||||
# Skips the CSRF check for stripe web hooks
|
# Skips the CSRF check for stripe web hooks
|
||||||
else
|
else
|
||||||
content_type :html, 'charset' => 'utf-8'
|
content_type :html, 'charset' => 'utf-8'
|
||||||
|
|
85
app/plan.rb
85
app/plan.rb
|
@ -100,11 +100,88 @@ get '/plan/thanks' do
|
||||||
erb :'plan/thanks'
|
erb :'plan/thanks'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
get '/plan/bitcoin/?' do
|
||||||
|
erb :'plan/bitcoin'
|
||||||
|
end
|
||||||
|
|
||||||
|
get '/plan/alternate/?' do
|
||||||
|
redirect '/plan/bitcoin'
|
||||||
|
end
|
||||||
|
|
||||||
|
def paypal_recurring_hash
|
||||||
|
{
|
||||||
|
ipn_url: "#{url}/webhooks/paypal",
|
||||||
|
description: 'Neocities Supporter',
|
||||||
|
amount: Site::PLAN_FEATURES[:supporter][:price].to_s,
|
||||||
|
currency: 'USD'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def paypal_recurring_authorization_hash
|
||||||
|
url = ENV['RACK_ENV'] == 'development' ? 'http://127.0.0.1:9292' : 'https://neocities.org'
|
||||||
|
paypal_recurring_hash.merge(
|
||||||
|
return_url: "#{url}/plan/paypal/return",
|
||||||
|
cancel_url: "#{url}/plan",
|
||||||
|
ipn_url: "#{url}/webhooks/paypal"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
get '/plan/paypal' do
|
||||||
|
require_login
|
||||||
|
|
||||||
|
hash = paypal_recurring_authorization_hash
|
||||||
|
|
||||||
|
if current_site.paypal_token
|
||||||
|
hash.merge! token: current_site.paypal_token
|
||||||
|
end
|
||||||
|
|
||||||
|
ppr = PayPal::Recurring.new hash
|
||||||
|
|
||||||
|
paypal_response = ppr.checkout
|
||||||
|
|
||||||
|
redirect paypal_response.checkout_url if paypal_response.valid?
|
||||||
|
end
|
||||||
|
|
||||||
|
get '/plan/paypal/return' do
|
||||||
|
require_login
|
||||||
|
|
||||||
|
if params[:TOKEN].nil? || params[:PAYERID].nil?
|
||||||
|
flash[:error] = 'Unknown error, could not complete the request. Please contact Neocities support.'
|
||||||
|
end
|
||||||
|
|
||||||
|
ppr = PayPal::Recurring.new(paypal_recurring_hash.merge(
|
||||||
|
token: params[:TOKEN],
|
||||||
|
payer_id: params[:PAYERID]
|
||||||
|
))
|
||||||
|
|
||||||
|
paypal_response = ppr.request_payment
|
||||||
|
unless paypal_response.approved? && paypal_response.completed?
|
||||||
|
flash[:error] = 'Unknown error, could not complete the request. Please contact Neocities support.'
|
||||||
|
redirect '/plan'
|
||||||
|
end
|
||||||
|
|
||||||
|
ppr = PayPal::Recurring.new(paypal_recurring_authorization_hash.merge(
|
||||||
|
frequency: 1,
|
||||||
|
token: params[:TOKEN],
|
||||||
|
period: :monthly,
|
||||||
|
reference: current_site.id.to_s,
|
||||||
|
payer_id: params[:PAYERID],
|
||||||
|
start_at: Time.now,
|
||||||
|
failed: 3,
|
||||||
|
outstanding: :next_billing
|
||||||
|
))
|
||||||
|
|
||||||
|
paypal_response = ppr.create_recurring_profile
|
||||||
|
|
||||||
|
current_site.paypal_token = params[:TOKEN]
|
||||||
|
current_site.paypal_profile_id = paypal_response.profile_id
|
||||||
|
current_site.paypal_active = true
|
||||||
|
current_site.save_changes validate: false
|
||||||
|
|
||||||
|
redirect '/plan/thanks-paypal'
|
||||||
|
end
|
||||||
|
|
||||||
get '/plan/thanks-paypal' do
|
get '/plan/thanks-paypal' do
|
||||||
require_login
|
require_login
|
||||||
erb :'plan/thanks-paypal'
|
erb :'plan/thanks-paypal'
|
||||||
end
|
end
|
||||||
|
|
||||||
get '/plan/alternate/?' do
|
|
||||||
erb :'/plan/alternate'
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,4 +1,16 @@
|
||||||
post '/stripe_webhook' do
|
post '/webhooks/paypal' do
|
||||||
|
EmailWorker.perform_async({
|
||||||
|
from: 'web@neocities.org',
|
||||||
|
to: 'errors@neocities.org',
|
||||||
|
subject: "[Neocities Paypal Webhook] Received a Webhook from Paypal",
|
||||||
|
body: params.inspect,
|
||||||
|
no_footer: true
|
||||||
|
})
|
||||||
|
|
||||||
|
'ok'
|
||||||
|
end
|
||||||
|
|
||||||
|
post '/webhooks/stripe' do
|
||||||
event = JSON.parse request.body.read
|
event = JSON.parse request.body.read
|
||||||
if event['type'] == 'customer.created'
|
if event['type'] == 'customer.created'
|
||||||
username = event['data']['object']['description'].split(' - ').first
|
username = event['data']['object']['description'].split(' - ').first
|
|
@ -12,6 +12,9 @@ development:
|
||||||
proxy_pass: 'somethinglongandrandom'
|
proxy_pass: 'somethinglongandrandom'
|
||||||
email_unsubscribe_token: 'somethingrandom'
|
email_unsubscribe_token: 'somethingrandom'
|
||||||
logs_path: /path/to/nginx/logs
|
logs_path: /path/to/nginx/logs
|
||||||
|
paypal_api_username: derp
|
||||||
|
paypal_api_password: ing
|
||||||
|
paypal_api_signature: tonz
|
||||||
test:
|
test:
|
||||||
database: 'postgres://neocities@localhost/neocities_test'
|
database: 'postgres://neocities@localhost/neocities_test'
|
||||||
database_pool: 1
|
database_pool: 1
|
||||||
|
@ -25,3 +28,6 @@ test:
|
||||||
ip_hash_salt: "400$8$1$fc21863da5d531c1"
|
ip_hash_salt: "400$8$1$fc21863da5d531c1"
|
||||||
proxy_pass: 'somethinglongandrandom'
|
proxy_pass: 'somethinglongandrandom'
|
||||||
email_unsubscribe_token: 'somethingrandom'
|
email_unsubscribe_token: 'somethingrandom'
|
||||||
|
paypal_api_username: derp
|
||||||
|
paypal_api_password: ing
|
||||||
|
paypal_api_signature: tonz
|
||||||
|
|
|
@ -7,3 +7,6 @@ phantomjs_url:
|
||||||
- http://localhost:8910
|
- http://localhost:8910
|
||||||
ip_hash_salt: "400$8$1$fc21863da5d531c1"
|
ip_hash_salt: "400$8$1$fc21863da5d531c1"
|
||||||
email_unsubscribe_token: "somethingrandomderrrrp"
|
email_unsubscribe_token: "somethingrandomderrrrp"
|
||||||
|
paypal_api_username: derp
|
||||||
|
paypal_api_password: ing
|
||||||
|
paypal_api_signature: tonz
|
||||||
|
|
|
@ -131,3 +131,10 @@ if ENV['RACK_ENV'] != 'development'
|
||||||
# Sass::Plugin.options[:never_update] = true
|
# Sass::Plugin.options[:never_update] = true
|
||||||
Sass::Plugin.options[:full_exception] = false
|
Sass::Plugin.options[:full_exception] = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
PayPal::Recurring.configure do |config|
|
||||||
|
config.sandbox = false
|
||||||
|
config.username = $config['paypal_api_username']
|
||||||
|
config.password = $config['paypal_api_password']
|
||||||
|
config.signature = $config['paypal_api_signature']
|
||||||
|
end
|
||||||
|
|
11
migrations/057_add_paypal_data.rb
Normal file
11
migrations/057_add_paypal_data.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
Sequel.migration do
|
||||||
|
up {
|
||||||
|
DB.add_column :sites, :paypal_profile_id, String
|
||||||
|
DB.add_column :sites, :paypal_token, String
|
||||||
|
}
|
||||||
|
|
||||||
|
down {
|
||||||
|
DB.drop_column :sites, :paypal_profile_id
|
||||||
|
DB.drop_column :sites, :paypal_token
|
||||||
|
}
|
||||||
|
end
|
9
migrations/058_add_paypal_status.rb
Normal file
9
migrations/058_add_paypal_status.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
Sequel.migration do
|
||||||
|
up {
|
||||||
|
DB.add_column :sites, :paypal_active, :boolean, default: false
|
||||||
|
}
|
||||||
|
|
||||||
|
down {
|
||||||
|
DB.drop_column :sites, :paypal_active
|
||||||
|
}
|
||||||
|
end
|
|
@ -19,6 +19,10 @@ describe '/plan' do
|
||||||
StripeMock.stop
|
StripeMock.stop
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'should work for paypal' do
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
it 'should work for fresh signup' do
|
it 'should work for fresh signup' do
|
||||||
visit '/plan'
|
visit '/plan'
|
||||||
fill_in 'Card Number', with: '4242424242424242'
|
fill_in 'Card Number', with: '4242424242424242'
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
<%
|
|
||||||
|
|
||||||
%>
|
|
||||||
|
|
||||||
<section class="section plans">
|
<section class="section plans">
|
||||||
<% if request.path.match /\/welcome/ %>
|
<% if request.path.match /\/welcome/ %>
|
||||||
<div class="txt-Center"><img src="/img/heartcat.png"></div>
|
<div class="txt-Center"><img src="/img/heartcat.png"></div>
|
||||||
|
@ -83,7 +79,7 @@
|
||||||
<%== plan_pricing_button :supporter %>
|
<%== plan_pricing_button :supporter %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong><%= Site::PLAN_FEATURES[:supporter][:space].to_space_pretty %></strong> storage</li>
|
<li><strong><%= (Site::PLAN_FEATURES[:supporter][:space] / (10**6)).to_comma_separated %> MB</strong> storage</li>
|
||||||
<li><strong><%= Site::PLAN_FEATURES[:supporter][:bandwidth].to_space_pretty %></strong> bandwidth</li>
|
<li><strong><%= Site::PLAN_FEATURES[:supporter][:bandwidth].to_space_pretty %></strong> bandwidth</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul>
|
<ul>
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
<style>
|
|
||||||
h3 { margin-top: 20px; }
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="header-Outro">
|
|
||||||
<div class="row content single-Col">
|
|
||||||
<h1>Alternate Payment Options</h1>
|
|
||||||
<h3 class="subtitle">Not everyone has a credit card, so we support many different types of payment.</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content single-Col misc-page txt-Center">
|
|
||||||
<article>
|
|
||||||
<section>
|
|
||||||
<h2>PayPal</h2>
|
|
||||||
<p>For people that can't use a Credit Card, we support PayPal for supporter upgrades.</p>
|
|
||||||
|
|
||||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
|
|
||||||
<input type="hidden" name="cmd" value="_s-xclick">
|
|
||||||
<input type="hidden" name="hosted_button_id" value="DFQBR5LNS3NW8">
|
|
||||||
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_subscribeCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
|
|
||||||
<img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<h2>The Bitcoin Lifer Plan</h2>
|
|
||||||
<p>
|
|
||||||
We don't have a good way to charge recurring with Bitcoin. So instead, we've put together the <strong>lifer plan</strong>. Basically, if you send us three years worth of supporter ($180USD), we'll never charge you again for the supporter upgrade, and you'll basically get it forever!
|
|
||||||
</p>
|
|
||||||
<p>Be sure to <a href="/contact">contact us</a> after sending with the transaction ID so we can confirm and upgrade your account.</p>
|
|
||||||
|
|
||||||
<p><img src="/img/bitcoin.png"></p>
|
|
||||||
<p><b>1MPYyfoWK8CkAzhLzLLtZGa9dwEGT5dm2T</b></p>
|
|
||||||
|
|
||||||
<h2>Gratipay:</h2>
|
|
||||||
<script data-gratipay-username="neocities" src="//gttp.co/v1.js"></script>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
</div> <!-- end .content -->
|
|
30
views/plan/bitcoin.erb
Normal file
30
views/plan/bitcoin.erb
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<style>
|
||||||
|
h3 { margin-top: 20px; }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="header-Outro">
|
||||||
|
<div class="row content single-Col">
|
||||||
|
<h1>Bitcoin</h1>
|
||||||
|
<h3 class="subtitle">Yes, Neocities accepts Bitcoin!</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content single-Col misc-page txt-Center">
|
||||||
|
<article>
|
||||||
|
<section>
|
||||||
|
<h2>The Bitcoin Homesteader Plan</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
We don't have a good way to charge recurring with Bitcoin (that adheres to Bitcoin's decentralized principles). So instead, we've put together a <strong>lifer plan</strong>. Basically, if you send us <strong>$50</strong> worth of BTC, we'll never charge you again for the supporter upgrade, and you'll basically get it forever!
|
||||||
|
</p>
|
||||||
|
<p>Be sure to <a href="/contact">contact us</a> after sending with the transaction ID so we can confirm and upgrade your account.</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
We don't cash out the Bitcoin. Many of our providers accept Bitcoin, and we use it to pay them.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><img src="/img/bitcoin.png"></p>
|
||||||
|
<p><b>1MPYyfoWK8CkAzhLzLLtZGa9dwEGT5dm2T</b></p>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
</div> <!-- end .content -->
|
|
@ -96,7 +96,7 @@
|
||||||
<a href="/" class="btn-Action" onclick="$('#upgradeForm').submit(); return false">
|
<a href="/" class="btn-Action" onclick="$('#upgradeForm').submit(); return false">
|
||||||
Upgrade for $<%= Site::PLAN_FEATURES[:supporter][:price] %>/mo
|
Upgrade for $<%= Site::PLAN_FEATURES[:supporter][:price] %>/mo
|
||||||
</a>
|
</a>
|
||||||
or pay with <a href="/plan/alternate/">PayPal</a> or <a href="/plan/alternate/">Bitcoin</a>
|
or pay with <strong><a href="/plan/paypal">PayPal</a></strong> or <strong><a href="/plan/bitcoin">Bitcoin</a></strong>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Add table
Reference in a new issue