GithubHelp home page GithubHelp logo

fsprgembeddedstoremac's Introduction

Introduction

FastSpring offers an innovative e-commerce engine designed to overcome ease of use, customer service, and cost issues that have plagued software e-commerce companies.

FastSpring's embedded store consists of a controller with some integration points and WebKit's web view. It's thin and very flexible and lets you integrate FastSpring the way that fits best for your application.

To get an idea of how it works, the SDK provides two examples and a test application. All source code is released under the MIT license. It is open to contributions and its use is unrestricted. See RELEASE_NOTES.html for the latest changes.

FsprgEmbeddedStore

FsprgEmbeddedStore consists mainly of the FsprgEmbeddedStoreController and its delegate protocol, FsprgEmbeddedStoreDelegate.

The FsprgEmbeddedStoreController controls the connected WebView (WebKit). It provides functionality to load the store, to monitor the page loading progress and to test if the current connection is secure (https).

@interface FsprgEmbeddedStoreController : NSObject
	- (WebView *)webView;
	- (void)setWebView:(WebView *)aWebView;
	- (id <FsprgEmbeddedStoreDelegate>)delegate;
	- (void)setDelegate:(id <FsprgEmbeddedStoreDelegate>)aDelegate;

	- (void)loadWithParameters:(FsprgStoreParameters *)parameters;
	- (void)loadWithContentsOfFile:(NSString *)aPath;
	- (BOOL)isLoading;
	- (double)estimatedLoadingProgress;
	- (BOOL)isSecure;
	- (NSString *)storeHost;
@end

In addition, it has some integration points defined by the FsprgEmbeddedStoreDelegate protocol. It gives notification of the initial load of the store, of subsequent page loads and of order completion. There's also the possibility to define a view to present the order confirmation to the user.

typedef enum {
	FsprgPageFS,
	FsprgPagePayPal,
	FsprgPageUnknown
} FsprgPageType;

@protocol FsprgEmbeddedStoreDelegate <NSObject>
	- (void)didLoadStore:(NSURL *)url;
	- (void)didLoadPage:(NSURL *)url ofType:(FsprgPageType)pageType;
	- (void)didReceiveOrder:(FsprgOrder *)order;
	- (NSView *)viewWithFrame:(NSRect)frame forOrder:(FsprgOrder *)order;
	- (void)webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame;
	- (void)webView:(WebView *)sender didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame;
@end

How-to embed it

Use our CocoaPod or perform the following steps.

  1. Clone or fork our repository.
  2. Open your project in Xcode.
  3. Perform File/Add Files to "Your App"... and choose the directory FsprgEmbeddedStoreMac/FsprgEmbeddedStore. We suggest to uncheck the option Copy items into destination group's folder.
  4. Delete reference to FsprgEmbeddedStoreMac/FsprgEmbeddedStore/Tests (in your project) if you don't need FsprgEmbeddedStore unit tests.
  5. Add WebKit.framework and Security.framework to your Targets.

How-to use it

  1. Read our Integration Guide to learn how to enable your store for FsprgEmbeddedStore requests.
  2. Create an AppController and use FsprgEmbeddedStoreController. Example1.app and Example2.app are showing how to implement an AppController in detail.
  3. Open MainMenu.nib.
  4. Create an instance of AppController inside Interface Builder.
  5. Drag WebKit control onto screen and connect it to the storeView Outlet of AppController.

Example: AppController.h

@interface AppController : NSObject <FsprgEmbeddedStoreDelegate> {
	IBOutlet WebView* storeView;
	FsprgEmbeddedStoreController *storeController;
}

- (FsprgEmbeddedStoreController *)storeController;
- (void)setStoreController:(FsprgEmbeddedStoreController *)aStoreController;

- (IBAction)load:(id)sender;

@end

How-to provide a link to the web-store

Sometimes users prefer to use a web-store instead of an embedded-store. Use FsprgStoreParameters to build the web-store URL and open it inside the default browser by using NSWorkspace.

- (IBAction)openWebStoreInBrowser:(id)sender
{
	FsprgStoreParameters *parameters = [FsprgStoreParameters parameters];
	[parameters setOrderProcessType:kFsprgOrderProcessDetail];
	[parameters setStoreId:@"your_store" withProductId:@"your_product"];
	[parameters setMode:kFsprgModeTest];
	[[NSWorkspace sharedWorkspace] openURL:[parameters toURL]];
}

FsprgOrder API

The FsprgOrder object represents the order confirmation returned via FsprgEmbeddedStoreDelegate protocol. To spare you plunging through the headers the following sections contain a real-life example and a compressed API documentation of FsprgOrder and its referred classes.

Example

Here's a real-life example to show the most common case of grabbing the serial number from the fulfilled license. Thanks to Greg Scown from SmileOnMyMac for sharing.

- (void)didReceiveOrder:(FsprgOrder *)order
{
   NSEnumerator *e = [[order orderItems] objectEnumerator];
   FsprgOrderItem *item = nil;
   while (item = [e nextObject]) {
       if ([[item productName] hasPrefix:@"MyItemNamePrefix"]) {
           NSString *userName = [[item license] licenseName];
           NSString *serialNumber = [[item license] firstLicenseCode];
           if ([[[item productName] lowercaseString] rangeOfString:@"upgrade"].location != NSNotFound) {
               NSLog(@"Upgrade purchase:\nName: %@\nSerial #: %@", userName, serialNumber);
           } else {
               NSLog(@"Full purchase:\nName: %@\nSerial #: %@",	userName, serialNumber);
           }
       }
   }
}

FsprgOrder.h

- (BOOL)orderIsTest;
- (NSString *)orderReference;
- (NSString *)orderLanguage;
- (NSString *)orderCurrency;
- (NSNumber *)orderTotal;
- (NSNumber *)orderTotalUSD;
- (NSString *)customerFirstName;
- (NSString *)customerLastName;
- (NSString *)customerCompany;
- (NSString *)customerEmail;
- (FsprgOrderItem *)firstOrderItem; // Shortcut for [[self orderItems] objectAtIndex:0].
- (NSArray *)orderItems;

FsprgOrderItem.h

- (NSString *)productName;
- (NSString *)productDisplay;
- (NSNumber *)quantity;
- (NSNumber *)itemTotal;
- (NSNumber *)itemTotalUSD;
- (NSString *)subscriptionReference; // See https://support.fastspring.com/entries/236487-api-subscriptions
- (NSURL *)subscriptionCustomerURL; // This URL can be presented to the customer to manage their subscription.
- (FsprgFulfillment *)fulfillment;
- (FsprgLicense *)license;           // Shortcut for [[self fulfillment] valueForKey:@"license"]
- (FsprgFileDownload *)download;     // Shortcut for [[self fulfillment] valueForKey:@"download"]

FsprgFulfillment.h

/*!
 * @param aKey type of fulfillment (e.g. license, download)
 * @result Specific fulfillment information (FsprgLicense, FsprgFileDownload).
 */
- (id)valueForKey:(NSString *)aKey;

FsprgLicense.h

- (NSString *)licenseName;
- (NSString *)licenseEmail;
- (NSString *)licenseCompany;
- (NSString *)firstLicenseCode;
- (NSArray *)licenseCodes;
- (NSDictionary *)licensePropertyList;
- (NSURL *)licenseURL;

FsprgFileDownload.h

- (NSURL *)fileURL;

Example1.app

Example1 app defaults contact fields by accessing MacOS' AddressBook. The order confirmation is a View XIB built inside Interface Builder.

Example1.app Screenshot

How-to implement the AppController

  • Set self as delegate on init
  • Set webView to FsprgEmbeddedStoreController on awakeFromNib
  • Delegate load: to loadWithParameters: of FsprgEmbeddedStoreController
  • Implement viewWithFrame:forOrder: by using a NSViewController (here OrderViewController) that uses the View XIB defined inside Interface Builder

Extract from AppController.h

@implementation AppController

- (id) init
{
	self = [super init];
	if (self != nil) {
		[self setStoreController:[[[FsprgEmbeddedStoreController alloc] init] autorelease]];
		[[self storeController] setDelegate:self];
	}
	return self;
}

- (void)awakeFromNib
{
	[[self storeController] setWebView:storeView];
	[self load:nil];
}

- (IBAction)load:(id)sender
{
	FsprgStoreParameters *parameters = [FsprgStoreParameters parameters];
	...
	[[self storeController] loadWithParameters:parameters];
}

- (NSView *)viewWithFrame:(NSRect)frame forOrder:(FsprgOrder *)order
{
	OrderViewController *orderViewController = [[OrderViewController alloc] initWithNibName:@"OrderView" bundle:nil];
	[orderViewController setRepresentedObject:order];

	[[orderViewController view] setFrame:frame];
	return [orderViewController view];
}

@end

How-to create the View XIB

  • Create class OrderViewController by extending NSViewController
  • Create View XIB
  • Set File’s Owner class to OrderViewController
  • Assign File’s Owner view Outlet to the main "Custom View"
  • Bind controls (e.g. label) to File’s Owner representedObject (= FSOrder) to present order confirmation data to the user

Example2.app

Example2 presents the order confirmation by using HTML, CSS and JavaScript. It uses Matt Gemmell's MGTemplateEngine to render the HTML.

Example2.app Screenshot

How-To create order HTML view

The AppController looks like the one in Example1. The only difference is the viewWithFrame:forOrder: implementation. It uses WebFrame's loadHTMLString:baseURL: method to load the HTML and present it to the user.

Extract from AppController.h

@implementation AppController

- (NSView *)viewWithFrame:(NSRect)frame forOrder:(FsprgOrder *)order
{
	MGTemplateEngine *engine = [MGTemplateEngine templateEngine];
	[engine setMatcher:[ICUTemplateMatcher matcherWithTemplateEngine:engine]];

	NSString *templatePath = [[NSBundle mainBundle] pathForResource:@"OrderView" ofType:@"html"];
	NSDictionary *variables = [NSDictionary dictionaryWithObject:order forKey:@"order"];
	NSString *htmlString = [engine processTemplateInFileAtPath:templatePath withVariables:variables];

	NSString *templateDirectory = [templatePath substringToIndex:[templatePath length]-[[templatePath lastPathComponent] length]];
	NSURL *baseURL = [NSURL URLWithString:[NSString stringWithFormat:@"file://%@", templateDirectory]];

	WebFrame *webFrame = [[[WebView alloc] initWithFrame:frame] mainFrame];
	[webFrame loadHTMLString:htmlString baseURL:baseURL];

	return [webFrame frameView];
}

@end

As we set FsprgOrder to a variable we can now conveniently access the order information inside the template. The baseURL points to the Resource directory. Thus, we can access CSS files to style the view and JavaScript to add some behavior and nice effects.

OrderView.html

<html>
	<head>
		<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />
		<title>Your Order</title>
		<link rel="stylesheet" type="text/css" href="OrderView.css">
		<script src="jquery-1.4.2.js"></script>
		<script language="javascript">
			$(function() {
				$(".orderItemsTitle").fadeIn(2000);
				$(".orderItem").fadeIn(2000);
			});
		</script>
	</head>
	<body>
		<div class="thankYouNote">Thanks for your order {{ order.customerFirstName }}!</div>
		<div class="orderItemsTitle">Ordered items</div>
		{% for orderItem in order.orderItems %}
		<div class="orderItem">
			<div class="productName">
			{{ orderItem.productName }}
			{% if orderItem.quantity > 1 %} ({{ orderItem.quantity }}) {% /if %}
			</div>
			<div class="licenseKey">Your license key: {{ orderItem.license.firstLicenseCode }}</div>
		</div>
		{% /for %}
	</body>
</html>

Test.app

The Test application lets you explore FastSpring's parameters and shows you the native order confirmation result (XML plist format).

Test.app Settings Screenshot  Test.app Results Screenshot

You can also store that confirmation result as a plist file and load it by using the FsprgEmbeddedStoreController's loadWithContentsOfFile: method. It simplifies the development and testing of the order confirmation view.

fsprgembeddedstoremac's People

Contributors

eelco avatar js avatar ksuther avatar rdewell avatar rickfillion avatar sdegutis avatar starkos avatar steiger avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fsprgembeddedstoremac's Issues

No embedded store

I don't want to have a store embedded in my app. An example on how to use fastspring without would be great.

Xcode warnings present

Hi! When I started using Xcode 10, I took the opportunity to update my project to use your latest version of the SDK. However, some warnings have now appeared. Below I am including them and the code changes necessary to make them disappear:

In file FsprgEmbeddedStoreController.m, the following line, produces error: Macro expansion producing 'defined' has undefined behaviour:

#define RETRIEVE_SSL_CERTIFICATES defined(MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6

Fixed by changing code to:

#if defined(MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
    #define RETRIEVE_SSL_CERTIFICATES 1
#else
    #define RETRIEVE_SSL_CERTIFICATES 0
#endif

In class FsprgOrder, selector orderFromData, variable declaration errorDesc produces warning: Unused variable 'errorDesc'. It can be fixed by moving it inside the #if-then section, from:

NSString *errorDesc = nil;

#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6
    

to

#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6
    NSString *errorDesc = nil;

I would appreciate if you could incorporate these changes, so my code can be in sync with your repository. Thanks!

Double Charges

Some customers reported double charges after Yosemite has been released in conjunction with the latest embedded SDK (1.0.12).

Store image assets don't load properly with App Transport Security on El Capitan

The problem occurs when compiling against the 10.11 SDK with Xcode 7, which enables Apple's new App Transport Security, disabling non-secure HTTP loads by default.

https://developer.apple.com/library/prerelease/ios/technotes/App-Transport-Security-Technote/?cm_mc_uid=07998743765214248868379&cm_mc_sid_50200000=1442426628

While the page itself loads OK, since it's connecting to https://sites.fastspring.com, some of the images in the page do not load. In the example screenshot, the icon for the app being sold is missing, as well as icons in the "Order Now" button, the icon by the language selection menu, etc.

screen shot 2015-09-25 at 5 48 09 pm

This also causes error messages like the following to be printed to the console, indicating that there are connections being blocked by ATS.

2015-09-25 17:45:51.839 PowerPhotos[15923:21862984] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)

This can be worked around by adding the NSAppTransportSecurity and NSAllowArbitraryLoads keys in your Info.plist file, but it should be made possible to load the embedded store without needing to make any exceptions in the Info.plist.

[FsprgStoreParameters toURL] returns URL that doesn't work

In code like this (taken from the provided examples):

FsprgStoreParameters *parameters = [FsprgStoreParameters parameters];
[parameters setOrderProcessType:kFsprgOrderProcessDetail];
[parameters setStoreId:@"poedit" withProductId:@"poedit-pro"];

the toURL method returns an URL such as https://sites.fastspring.com/poedit/product/poedit-pro — i.e. an HTTPS URL. But the FastSpring store cannot server this page via HTTPS and demands HTTP access — the page shown says this:

Secure Connection Not Allowed

This resource may not be accessed with a secure (https) connection.
Use a normal connection method (http) instead.

So the officially documented method of opening the store in a browser doesn't work. This should either return a http:// URL or — and that would be my preference — sites.fastspring.com should just serve the same content over both HTTP and HTTPS.

To be honest, this whole Secure Connection Not Allowed thing seems rather weird to me and doesn't make much sense to me. Especially now that Google is pushing for HTTPS-only web, the first store page really should be HTTPS...

WebView deprecated on 10.14 and later

When compiling the FsprgEmbeddedStoreMac framework with a deployment target of 10.14 or later, Xcode outputs a list of deprecation warnings against WebView, such as:

FsprgEmbeddedStoreDelegate.h:61:18: warning: 'WebView' is deprecated: first deprecated in macOS 10.14 - No longer supported; please adopt WKWebView.
- (void)webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame;

The framework should be updated to use WKWebView before Apple drops support for WebView altogether.

Store doesn't load correctly on Yosemite

When running on Yosemite, the store page either doesn't finish loading at all (leading to eventual timeout, without displaying anything), or shows the initial page, but pressing the Order button in the embedded page (default style) fails with "The operation couldn’t be completed. (NSURLErrorDomain error -999.)".

Steps to reproduce:

  1. Open the Example1 project, replace dummy store+product with real ones.
  2. Compile and run.
  3. Observe that the store page doesn't load and the spinning progress indicator keeps spinning. Eventually, timeout is reported.

Curiously, Example2 (as well as my code) behave differently:

  1. The first page is loaded all right.
  2. Click Order either pops up the above mentioned error or, if it doesn't, clicking it on the next order page certainly will.

Test with DP6 (14A329f), affects both code compiled with Xcode 6 and existing binaries compiled with 5 for older versions.

Use ARC

Most new apps use ARC. It would be great to be able to put these files into a new Mac app and have them Just Work™, instead of having to manually disable ARC for each of them individually, which is currently required for using these in an ARC app.

I'm open to converting the files to ARC myself and submitting a pull request (assuming there's no technical restrictions from doing so). Would such a pull request be welcome?

Dangling Volume Pricing window

The "Volume Pricing" window remains open even though the main purchase window is still open.

Steps to reproduce

  1. Open the built-in web store with a product that has volume pricing.
  2. Click the "Volume Pricing Available" button.
  3. Close the web store window.

Expected result:

  • The Volume Pricing details pop-up window should also close as the main window is closed.

Actual result:

  • The Volume Pricing pop-up window remains open.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.