Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apple Pay - Donate form API calls #4619

Merged
merged 33 commits into from
Sep 27, 2023
Merged

Apple Pay - Donate form API calls #4619

merged 33 commits into from
Sep 27, 2023

Conversation

tonisevener
Copy link
Collaborator

@tonisevener tonisevener commented Sep 9, 2023

Phabricator:
https://phabricator.wikimedia.org/T345847

Notes

This PR adds the ability to do a few API calls for the donate form:

  1. Fetching the donate config file.
  2. Calling FR Tech's payment networks endpoint. (Still in development)
  3. Calling FRTech's submit payment endpoint. (Still in development)

Since these are pretty basic calls*, I thought this was a good chance to spin up a new service in WKData and avoid the loop back to the app for networking. This new service is called WKBasicService.

*FR Tech endpoints may eventually need an API key, but I don't have those details yet.

I am also fetching the config and payment methods endpoints at the same time as our current announcements endpoint fetch. I may modify it slightly in the future to trigger once our future campaign config call goes through, to reduce unnecessary calls.

Note: b3aea1b will come in handy while the FR Tech endpoints are still in development. So no need to revert before merging, but the goal is to eventually revert this commit.

Test Steps

  1. In the app and trigger an announcements fetch (generally just a background/foreground, then pull to refresh is needed on Explore). Confirm config values are printed out.
  2. Confirm unit tests pass.

@tonisevener tonisevener added the Dependent PR PR is dependent on another PR - merge dependent PR first and update branch before merging label Sep 9, 2023
@tonisevener tonisevener changed the title [HOLD] Apple Pay - Fetch payment networks and Donate form config [HOLD] Apple Pay - Fetch payment networks and donate form config Sep 9, 2023
@tonisevener tonisevener marked this pull request as draft September 18, 2023 21:47
@tonisevener tonisevener mentioned this pull request Sep 18, 2023
Base automatically changed from monorepo to main September 19, 2023 22:30
@tonisevener tonisevener force-pushed the apple-pay-1 branch 2 times, most recently from 980e910 to f5fce65 Compare September 22, 2023 20:01
@tonisevener tonisevener removed the Dependent PR PR is dependent on another PR - merge dependent PR first and update branch before merging label Sep 22, 2023
@tonisevener tonisevener changed the title [HOLD] Apple Pay - Fetch payment networks and donate form config Apple Pay - Donate form API calls Sep 22, 2023
@tonisevener tonisevener marked this pull request as ready for review September 22, 2023 20:16
@tonisevener tonisevener requested review from a team and mazevedofs and removed request for a team September 22, 2023 20:16
Copy link
Contributor

@staykids staykids left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a big chunk of work to review here. Here's just very superficial stuff from my first pass. Will dig deeper in next pass.

import Foundation

extension Int {
var isHttpSuccess: Bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the intention, and appreciate it only internal to WKData, but I wonder if this really needs to extend all Ints over just being in some generalized HTTP status code utility.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea - I moved this into a HTTPURLResponse extension.

Comment on lines 5 to 7
var components = URLComponents()
components.scheme = "https"
components.path = "/w/api.php"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to generalize out these repeated constructions into a WMFAPIPath-ish from NSURL+WMFLinkParsing thing (BaseMediaWikiAPIPathComponents?)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done (I think) - let me know if it's what you had in mind!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify, I think even pulling the repeated scheme would be useful. Like:

private static var baseMediaWikiAPIPathComponents: URLComponents {
	var components = URLComponents()
	components.scheme = "https"
	components.path = "/w/api.php"
	return components
}

static func mediaWikiAPIURL(project: WKProject) -> URL? {
    var components = baseMediaWikiAPIPathComponents

    switch project {
    ...
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I missed this one - will make a note to put it in a later PR. Thanks!

Comment on lines 5 to 7
public let currencyMinimums: [String: Decimal]
public let currencyMaximums: [String: Decimal]
public let currencyAmounts7: [String: [Decimal]]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand these currently match the API, but just wanted to mention the same note as in Slack about potentially more precise naming here if those suggestions still agree with you.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! I updated the mock json and the config at donate.wikimedia.org...so far I haven't changed the config at test.wikipedia.org. I will EOD today once I know FR Tech isn't testing against it.

let response: Response
}

/// {"response":{"status":"Success","gateway_transaction_id":"PVLSL6JZ4NK2WN82","order_id":"1235.1"}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extraneous

import Foundation

/// Use this service for the most basic networking service calls. It does not handle authentication.
///
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extraneous

@@ -10,4 +10,11 @@ public struct FeatureFlags {
return true
}

public static var applePayEnabled: Bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, but what do you think about something like applePayDonationEnabled instead? On first read applePayEnabled connoted the availability of Apple Pay itself to me, rather than our intent of Apple Pay as a donation option.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done (twice actually, sorry). I went with the more general donorExperienceImrovementsEnabled - I'm hoping to be able to wrap up all of our improvements in the next few weeks around that for easy rollback, not just the native form.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏽 new name works great, much more precise about what it is

Copy link
Contributor

@staykids staykids left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tonisevener More notes below

switch result {
case .success(let response):
guard response.response.status == "Success" else {
return
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing a completion handler call in this failure case.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch! Done.

guard
let encodedName = key.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryComponentAllowed),
let encodedValue = (value as? String)?.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryComponentAllowed) else {
continue
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unsure if we want to continue our pattern of assert(...) in the new frameworks, but this may be a good candidate. Any key/value parameter pair failing to be properly URL encoded means the entire parameter dictionary is invalid, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed!

import Foundation

public protocol WKURLSession {
func wkDataTask(with request: URLRequest, completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void) -> WKURLSessionDataTask
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My limited understanding of some of the new Swift concurrency changes (actors/actor isolation/et al) makes it unclear to me if the @Sendable here and elsewhere is needed or not.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah - I am also unsure if it's entirely necessary. Since WKURLSession is meant to be a basic wrapper for easier unit testing (in WKBasicServiceTests.swift), it felt correct to directly match the signature of the URLSession method it wraps:

open func dataTask(with request: URLRequest, completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask

I'm open to alternative ways to mock this if it feels icky. This workaround came after first trying to mock via a URLSession subclass, and these types of warnings appeared - https://stackoverflow.com/questions/74406895/subclassing-urlsession-init-was-deprecated-in-ios-13-0.

import Foundation

/// Use this service for the most basic networking service calls. It does not handle authentication.
public final class WKBasicService: WKService {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you intending WKService to serve some more general purpose than effectively performing network requests? If not, WKNetworkService, WKBasicNetworkService, and WKNetworkServiceEnvironment read as far more precise type identifiers to me.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, only for performing network requests. Back when I worked on wikimedia/wikipedia-ios-data#6 I was hoping that "service" would become synonymous with a class that does networking (i.e. deals with a UrlSession internally) in our minds. I felt like adding "network" in the name might feel redundant or suggest that there are other types of services that do not involve networking. Not a big deal to me though, I've been in the weeds on these classes so far so if the name change makes more sense to fresh eyes I'm happy to change it. Do you think I should add it to other related structs/enums as well? (WKMediaWikiNetworkServiceRequest, WKNetworkServiceRequestMethod, etc?)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can certainly appreciate the desire to keep it short (WKService over WKNetworkService) but from my first read of this work it didn't quite click for me. I think I mentally expect Service as something more generalized, and there being specializations of it – like what if there's some local data service, or some string localization service? That's just my personal take!

For time sake let's leave this as-is and pick this conversation back up in the future as WKData expands in functionality – sound good?

@staykids staykids removed their assignment Sep 26, 2023
@staykids staykids self-assigned this Sep 27, 2023
import Foundation

/// Use this service for the most basic networking service calls. It does not handle authentication.
public final class WKBasicService: WKService {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can certainly appreciate the desire to keep it short (WKService over WKNetworkService) but from my first read of this work it didn't quite click for me. I think I mentally expect Service as something more generalized, and there being specializations of it – like what if there's some local data service, or some string localization service? That's just my personal take!

For time sake let's leave this as-is and pick this conversation back up in the future as WKData expands in functionality – sound good?

@staykids staykids removed their assignment Sep 27, 2023
@staykids staykids merged commit 9a0966f into main Sep 27, 2023
@staykids staykids deleted the apple-pay-1 branch September 27, 2023 19:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants