Skip to content

Instantly share code, notes, and snippets.

@jbmyid
Last active September 12, 2017 20:32
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save jbmyid/16c70067ea3f5cea7d2b to your computer and use it in GitHub Desktop.
Save jbmyid/16c70067ea3f5cea7d2b to your computer and use it in GitHub Desktop.
module PayPal::SDK
module REST
module DataTypes
Payer.class_eval do
object_of :merchant_id, String
end
class ChargeModel < Base
object_of :id, String
object_of :type, String
object_of :amount, Currency
end
class PaymentDefination < Base
object_of :name, String
object_of :type, String #UNLIMITED or FIXED
object_of :frequency_interval, String
object_of :frequency, String # DAY, WEEK, MONTH or YEAR.
object_of :cycles, Integer
object_of :amount, Currency
object_of :id, String
array_of :charge_models, ChargeModel
end
class MerchantPreference < Base
object_of :id, String
object_of :cancel_url, String
object_of :return_url, String
object_of :setup_fee, Currency
object_of :max_fail_attempts, Integer
object_of :auto_bill_amount, Currency
object_of :initial_fail_amount_action, Currency
object_of :create_time, DateTime
object_of :update_time, DateTime
end
class BillingPlans < Base
object_of :total_count, Integer
array_of :BillingPlan, Invoice
end
class BillingPlan < Base
# {
# name: "Plan Name", type: "INFINITE", description: "Any description",
# payment_definitions: {name: "Name", type: "REGULAR", frequency_interval: 1, amount: {value: 2.3, currency: "USD"}, frequency: "MONTH"},
# merchant_preferences: {cancel_url: "http://google.com", return_url: "http://google.com", setup_fee: {value: 0, currency: "USD"}}
# }
object_of :id, String
object_of :name, String
object_of :description, String
object_of :type, String
array_of :payment_definitions, DataTypes::PaymentDefination
object_of :merchant_preferences, MerchantPreference
object_of :state, String
array_of :links, DataTypes::Links
object_of :payee, DataTypes::Payer
object_of :create_time, DateTime
object_of :update_time, DateTime
def self.load_members
object_of :name, String
object_of :description, String
end
# def self.all(options={})
# path = "vi/payments/billing-plans"
# BillingPlans.new(api.get(path, options, BillingPlan.new.http_header))
# end
include RequestDataType
def create
path = "v1/payments/billing-plans"
response = api.post(path, self.to_hash)
self.merge!(response)
success?
end
def self.find(resource_id)
raise ArgumentError.new("id required") if resource_id.to_s.strip.empty?
path = "v1/payments/billing-plans/#{resource_id}"
self.new(api.get(path))
end
def update(attrs)
path = "v1/payments/billing-plans/#{self.id}"
attrs = attrs.blank? ? [{path: "/", value: {state: "ACTIVE"}, op: "replace"}] : attrs
response = api.patch(path, attrs)
self.merge!(response)
success?
end
def activate!
update([{path: "/", value: {state: "ACTIVE"}, op: "replace"}])
end
end
class AgreementDetails < Base
object_of :outstanding_balance, Currency
object_of :cycles_remaining, Integer
object_of :cycles_completed, Integer
object_of :final_payment_date, DateTime
object_of :failed_payment_count, Integer
end
class AgreeMent < Base
object_of :id, String
object_of :name, String
object_of :description, String
object_of :start_date, DateTime
object_of :payer, Payer
object_of :plan, BillingPlan
array_of :links, DataTypes::Links
object_of :override_merchant_preferences, MerchantPreference
object_of :state, String
object_of :agreement_details, AgreementDetails
include RequestDataType
def create
path = "v1/payments/billing-agreements"
response = api.post(path, self.to_hash)
self.merge!(response)
success?
end
def approval_url
links.select{|link| link.rel=="approval_url"}[0].try :href
end
def execute_url
links.select{|link| link.rel=="execute"}[0].try :href
end
def execute!(token)
response = api.post("v1/payments/billing-agreements/#{token}/agreement-execute")
self.merge!(response)
success?
end
def suspend
path = "v1/payments/billing-agreements/#{id}/suspend"
response = api.post(path, {note: "Suspending the subscription"})
self.merge!(response)
success?
end
def reactivate
path = "v1/payments/billing-agreements/#{id}/re-activate"
response = api.post(path, {note: "Reactivating the agreement."})
self.merge!(response)
success?
end
def cancel
path = "v1/payments/billing-agreements/#{id}/cancel"
response = api.post(path, {note: "Cencelling the agreement."})
self.merge!(response)
success?
end
# def update_plan(plan_id)
# path = "v1/payments/billing-agreements/#{id}"
# response = api.patch(path, [{op: "replace", path: "/", value: {plan: {id: plan_id}}}])
# self.merge!(response)
# success?
# end
def self.find(resource_id)
raise ArgumentError.new("id required") if resource_id.to_s.strip.empty?
path = "v1/payments/billing-agreements/#{resource_id}"
self.new(api.get(path))
end
end
end
end
end
# For paypal plan concern
module ActAsPaypalPlan
extend ActiveSupport::Concern
include PayPal::SDK::REST::DataTypes
included do
after_validation :set_paypal_plan, on: :create
end
def paypal_plan
if paypal_id
@paypal_plan ||= BillingPlan.find(paypal_id)
else
@paypal_plan ||= BillingPlan.new(name: name, type: "INFINITE", description: "Pending",
payment_definitions: {name: "PaymentDefinition#{name}", type: "REGULAR", frequency_interval: 1, amount: {value: price, currency: "USD"}, frequency: interval},
merchant_preferences: {cancel_url: "http://google.com", return_url: "http://google.com", setup_fee: {value: 0, currency: "USD"}})
end
end
def agreement(options={})
@agreement ||= AgreeMent.new(name: "Subscribe #{name}", description: "Subscribe #{name}", start_date: Date.today+1.days, plan: {id: paypal_plan.id}, payer: {payment_method: "paypal"}, override_merchant_preferences: {cancel_url: options[:cancel_url], return_url: options[:return_url]})
end
private
def set_paypal_plan
return if errors.any?
billing_plan = paypal_plan
if billing_plan.create && billing_plan.activate!
self.paypal_id = billing_plan.id
# billing_plan.activate!
else
self.errors[:paypal_id] << billing_plan.error[:details].collect{|e| e[:issue]}.join(",")
end
end
end
# for paypal subscription concern
module ActAsSubscription
TYPES = %w{visa mastercard amex discover maestro}
STATES = {cancelled: "CANCELLED", active: "ACTIVE", suspended: "SUSPENDED"}
extend ActiveSupport::Concern
include PayPal::SDK::REST::DataTypes
included do
define_callbacks :cancel
define_callbacks :reactivate
define_callbacks :suspend
after_create :active!
end
STATES.each do |k,v|
define_method "#{k}?" do
self.state == v
end
define_method "#{k}!" do
update_column(:state, v)
end
end
# def credit_card
# @credit_card ||= CreditCard.new
# end
# def credit_card=(card)
# @credit_card = card
# end
# def payer
# @payer ||= Payer.new(payment_method: "paypal")
# end
# def credit_card_token
# # , last4: credit_card.number[-4..-1], type: credit_card.type, expire_month: credit_card.expire_month, expire_year: credit_card.expire_year
# @credit_card_token ||= CreditCardToken.new({credit_card_id: credit_card.id})
# end
# def create_agreement
# # agreement.create({name: "Subscribe #{plan.name}", description: "Subscribe #{plan.name}", start_date: (Date.today+1.days), plan: {id: plan.paypal_plan.id}, payer: {payment_method: "paypal"}})
# agreement.create
# end
def agreement
@agreement ||= AgreeMent.find(agreement_id)
end
def cancel
return unless agreement_id
run_callbacks :cancel do
update_column(:state, STATES[:cancelled]) if agreement.cancel
end
end
def suspend
return unless agreement_id
run_callbacks :suspend do
update_column(:state, STATES[:suspended]) if agreement.suspend
end
end
def reactivate
return unless agreement_id
run_callbacks :reactivate do
update_column(:state, STATES[:active]) if agreement.reactivate
end
end
# def update_plan(plan)
# agreement.update_plan(plan.paypal_id)
# end
private
module ClassMethods
#
end
end
# licence creation or subscribing paypal plan
module PaypalLicence
extend ActiveSupport::Concern
included do
before_action :set_user
before_action :set_licence, only: [:destroy, :suspend, :reactivate]
end
def new
@licence = @user.licence || @user.build_licence
session[:plan_id] = nil
authorize @licence if need_to_authorise?
@plans = FixedPlan.all
end
def approve
authorize @user.build_licence, :new? if need_to_authorise?
@plan = Plan.find_by_id(params[:licence][:plan_id])
if @plan.agreement(cancel_url: paypal_cancel_url, return_url: paypal_return_url).create
session[:plan_id] = @plan.id
redirect_to @plan.agreement.approval_url
else
flash[:alert] = t("flash.error.licence_approve_failure")
redirect_to new_licence_path
end
end
def execute
authorize @user.build_licence, :new? if need_to_authorise?
if session[:plan_id]
@licence = @user.create_licence_with_payment_token!(token: params[:token], plan_id: session[:plan_id])
session[:plan_id] = nil
flash[:notice] = t("flash.success.licence.created")
redirect_to after_create_path
else
redirect_to unauthorized_url
end
end
def create
@licence = Licence.new_with_credit_card(licence_params, @user)
authorize @licence if need_to_authorise?
@plans = FixedPlan.all
if @licence.save
redirect_to after_create_path
else
render "new"
end
end
# Suspend Subscription and can be reactivated
def suspend
authorize @licence if need_to_authorise?
@licence.suspend
respond_with @licence, location: after_suspension_path
end
def reactivate
authorize @licence if need_to_authorise?
@licence.reactivate
respond_with @licence, location: after_reactivate_path
end
# Cancel Subscription
def destroy
authorize @licence if need_to_authorise?
@licence.destroy
respond_with @licence, location: after_cancellation_path
end
private
def after_suspension_path
raise NotImplementedError
end
def after_reactivate_path
raise NotImplementedError
end
def after_cancellation_path
raise NotImplementedError
end
def after_create_path
raise NotImplementedError
end
def paypal_cancel_url
# new_user_licence_url(@user, state: "cancelled")
raise NotImplementedError
end
def paypal_return_url
# execute_user_licence_url(@user, state: "success", )
raise NotImplementedError
end
def need_to_authorise?
true
end
def new_licence_path
# new_user_licence_path(@user)
raise NotImplementedError
end
def unauthorized_url
# user_path(@user)
raise NotImplementedError
end
def set_user
raise NotImplementedError
end
def set_licence
@licence = @user.licence
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment