Module: Spree::PaypalPlatform::Gateway::PaymentSessions

Extended by:
ActiveSupport::Concern
Included in:
Spree::PaypalPlatform::Gateway
Defined in:
app/models/spree/paypal_platform/gateway/payment_sessions.rb

Overview

Payment-session lifecycle used by Spree 5 storefronts.

Instance Method Summary collapse

Instance Method Details

#complete_payment_session(payment_session:, **_unused) ⇒ void

This method returns an undefined value.

Captures the PayPal order and creates the Spree payment.

Does not complete the order — that is handled by Carts::Complete.

Parameters:

  • payment_session (Spree::PaymentSessions::PaypalPlatform)
  • params (Hash)


95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'app/models/spree/paypal_platform/gateway/payment_sessions.rb', line 95

def complete_payment_session(payment_session:, **_unused)
  paypal_order_id = payment_session.external_id
  lookup = { 'id' => paypal_order_id, 'prefer' => 'return=representation' }

  fetched = client.orders.get_order(lookup)
  response =
    if fetched.data.status == 'COMPLETED'
      fetched
    else
      client.orders.capture_order(lookup)
    end

  payment_session.update!(external_data: response.data.as_json)

  payment_session.order.with_lock do
    if response.data.status == 'COMPLETED'
      payment_session.process if payment_session.can_process?

      payment = payment_session.find_or_create_payment!

      if payment.present? && !payment.completed?
        payment.started_processing! if payment.checkout?
        payment.complete! if payment.can_complete?
      end

      create_profile(payment) if payment&.source.present?

      payment_session.complete unless payment_session.completed?
    elsif payment_session.can_fail?
      payment_session.fail
    end
  end
rescue PaypalServerSdk::APIException => e
  payment_session.fail if payment_session.can_fail?
  raise Spree::Core::GatewayError, "PayPal API error: #{e.message}"
end

#create_payment_session(order:, amount: nil, **_unused) ⇒ Spree::PaymentSessions::PaypalPlatform, NilClass

Creates a PayPal order and persists a payment session.

Also requests a Card Fields client token. Failure is non-fatal — the wallet / Apple Pay buttons still work without it.

Parameters:

  • order (Spree::Order)
  • amount (Numeric, NilClass) (defaults to: nil)
  • external_data (Hash)

Returns:

  • (Spree::PaymentSessions::PaypalPlatform, NilClass)


39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'app/models/spree/paypal_platform/gateway/payment_sessions.rb', line 39

def create_payment_session(order:, amount: nil, **_unused)
  total = amount.presence || order.total_minus_store_credits

  return nil if total.zero?

  protect_from_error do
    order_presenter = OrderPresenter.new(order)
    paypal_response = client.orders.create_order(order_presenter.to_json)

    session_data = paypal_response.data.as_json

    session_data['enable_apple_pay'] = apple_pay_enabled?
    session_data['enable_card_fields'] = card_fields_enabled?

    if card_fields_enabled?
      client_token = generate_client_token
      session_data['client_token'] = client_token if client_token.present?
    end

    payment_session_class.create!(
      order: order,
      payment_method: self,
      amount: total,
      currency: order.currency,
      status: 'pending',
      external_id: paypal_response.data.id,
      customer: order.user,
      external_data: session_data
    )
  end
end

#parse_webhook_event(raw_body, headers) ⇒ Hash, NilClass

Parses an incoming PayPal webhook into a payment-session action.

Parameters:

  • raw_body (String)
  • headers (Hash)

Returns:

  • (Hash, NilClass)


139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'app/models/spree/paypal_platform/gateway/payment_sessions.rb', line 139

def parse_webhook_event(raw_body, headers)
  verify_webhook_signature!(raw_body, headers)

  event = JSON.parse(raw_body).with_indifferent_access
  event_type = event[:event_type]
  resource = event[:resource] || {}

  paypal_order_id = extract_order_id_from_webhook(event_type, resource)
  return nil unless paypal_order_id

  payment_session = Spree::PaymentSessions::PaypalPlatform.find_by(
    payment_method: self,
    external_id: paypal_order_id
  )
  return nil unless payment_session

  case event_type
  when 'CHECKOUT.ORDER.APPROVED'
    { action: :authorized, payment_session: payment_session, metadata: { paypal_event: event } }
  when 'PAYMENT.CAPTURE.COMPLETED'
    { action: :captured, payment_session: payment_session, metadata: { paypal_event: event } }
  when 'PAYMENT.CAPTURE.DENIED', 'PAYMENT.CAPTURE.DECLINED'
    { action: :failed, payment_session: payment_session, metadata: { paypal_event: event } }
  when 'PAYMENT.CAPTURE.REVERSED', 'PAYMENT.CAPTURE.REFUNDED'
    { action: :canceled, payment_session: payment_session, metadata: { paypal_event: event } }
  end
end

#payment_session_classClass

Returns:

  • (Class)


24
25
26
# File 'app/models/spree/paypal_platform/gateway/payment_sessions.rb', line 24

def payment_session_class
  Spree::PaymentSessions::PaypalPlatform
end

#session_required?TrueClass, FalseClass

Returns:

  • (TrueClass, FalseClass)


17
18
19
# File 'app/models/spree/paypal_platform/gateway/payment_sessions.rb', line 17

def session_required?
  true
end

#update_payment_session(payment_session:, amount: nil, external_data: {}) ⇒ TrueClass, ...

Parameters:

  • payment_session (Spree::PaymentSessions::PaypalPlatform)
  • amount (Numeric, NilClass) (defaults to: nil)
  • external_data (Hash) (defaults to: {})

Returns:

  • (TrueClass, FalseClass, NilClass)


77
78
79
80
81
82
83
84
# File 'app/models/spree/paypal_platform/gateway/payment_sessions.rb', line 77

def update_payment_session(payment_session:, amount: nil, external_data: {})
  attrs = {}
  attrs[:amount] = amount if amount.present?

  attrs[:external_data] = (payment_session.external_data || {}).merge(external_data.stringify_keys) if external_data.present?

  payment_session.update!(attrs) if attrs.any?
end