Unverified Commit a2fedada authored by Simon Esposito's avatar Simon Esposito Committed by GitHub
Browse files

Add IAP Validation APIs (#582)

Add runtime/api functions to validate Apple IAP receipts and Google/Huawei purchases.
parent 7fada7ee
Loading
Loading
Loading
Loading
+189 −152

File changed.

Preview size limit exceeded, changes collapsed.

+243 −0
Original line number Diff line number Diff line
@@ -3393,6 +3393,108 @@ func local_request_Nakama_UpdateGroup_0(ctx context.Context, marshaler runtime.M

}

func request_Nakama_ValidatePurchaseApple_0(ctx context.Context, marshaler runtime.Marshaler, client NakamaClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
	var protoReq api.ValidatePurchaseAppleRequest
	var metadata runtime.ServerMetadata

	newReader, berr := utilities.IOReaderFactory(req.Body)
	if berr != nil {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
	}
	if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
	}

	msg, err := client.ValidatePurchaseApple(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
	return msg, metadata, err

}

func local_request_Nakama_ValidatePurchaseApple_0(ctx context.Context, marshaler runtime.Marshaler, server NakamaServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
	var protoReq api.ValidatePurchaseAppleRequest
	var metadata runtime.ServerMetadata

	newReader, berr := utilities.IOReaderFactory(req.Body)
	if berr != nil {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
	}
	if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
	}

	msg, err := server.ValidatePurchaseApple(ctx, &protoReq)
	return msg, metadata, err

}

func request_Nakama_ValidatePurchaseGoogle_0(ctx context.Context, marshaler runtime.Marshaler, client NakamaClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
	var protoReq api.ValidatePurchaseGoogleRequest
	var metadata runtime.ServerMetadata

	newReader, berr := utilities.IOReaderFactory(req.Body)
	if berr != nil {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
	}
	if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
	}

	msg, err := client.ValidatePurchaseGoogle(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
	return msg, metadata, err

}

func local_request_Nakama_ValidatePurchaseGoogle_0(ctx context.Context, marshaler runtime.Marshaler, server NakamaServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
	var protoReq api.ValidatePurchaseGoogleRequest
	var metadata runtime.ServerMetadata

	newReader, berr := utilities.IOReaderFactory(req.Body)
	if berr != nil {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
	}
	if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
	}

	msg, err := server.ValidatePurchaseGoogle(ctx, &protoReq)
	return msg, metadata, err

}

func request_Nakama_ValidatePurchaseHuawei_0(ctx context.Context, marshaler runtime.Marshaler, client NakamaClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
	var protoReq api.ValidatePurchaseHuaweiRequest
	var metadata runtime.ServerMetadata

	newReader, berr := utilities.IOReaderFactory(req.Body)
	if berr != nil {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
	}
	if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
	}

	msg, err := client.ValidatePurchaseHuawei(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
	return msg, metadata, err

}

func local_request_Nakama_ValidatePurchaseHuawei_0(ctx context.Context, marshaler runtime.Marshaler, server NakamaServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
	var protoReq api.ValidatePurchaseHuaweiRequest
	var metadata runtime.ServerMetadata

	newReader, berr := utilities.IOReaderFactory(req.Body)
	if berr != nil {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
	}
	if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
	}

	msg, err := server.ValidatePurchaseHuawei(ctx, &protoReq)
	return msg, metadata, err

}

func request_Nakama_WriteLeaderboardRecord_0(ctx context.Context, marshaler runtime.Marshaler, client NakamaClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
	var protoReq api.WriteLeaderboardRecordRequest
	var metadata runtime.ServerMetadata
@@ -5179,6 +5281,75 @@ func RegisterNakamaHandlerServer(ctx context.Context, mux *runtime.ServeMux, ser

	})

	mux.Handle("POST", pattern_Nakama_ValidatePurchaseApple_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
		ctx, cancel := context.WithCancel(req.Context())
		defer cancel()
		var stream runtime.ServerTransportStream
		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/nakama.api.Nakama/ValidatePurchaseApple")
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}
		resp, md, err := local_request_Nakama_ValidatePurchaseApple_0(rctx, inboundMarshaler, server, req, pathParams)
		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
		ctx = runtime.NewServerMetadataContext(ctx, md)
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}

		forward_Nakama_ValidatePurchaseApple_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)

	})

	mux.Handle("POST", pattern_Nakama_ValidatePurchaseGoogle_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
		ctx, cancel := context.WithCancel(req.Context())
		defer cancel()
		var stream runtime.ServerTransportStream
		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/nakama.api.Nakama/ValidatePurchaseGoogle")
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}
		resp, md, err := local_request_Nakama_ValidatePurchaseGoogle_0(rctx, inboundMarshaler, server, req, pathParams)
		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
		ctx = runtime.NewServerMetadataContext(ctx, md)
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}

		forward_Nakama_ValidatePurchaseGoogle_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)

	})

	mux.Handle("POST", pattern_Nakama_ValidatePurchaseHuawei_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
		ctx, cancel := context.WithCancel(req.Context())
		defer cancel()
		var stream runtime.ServerTransportStream
		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/nakama.api.Nakama/ValidatePurchaseHuawei")
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}
		resp, md, err := local_request_Nakama_ValidatePurchaseHuawei_0(rctx, inboundMarshaler, server, req, pathParams)
		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
		ctx = runtime.NewServerMetadataContext(ctx, md)
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}

		forward_Nakama_ValidatePurchaseHuawei_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)

	})

	mux.Handle("POST", pattern_Nakama_WriteLeaderboardRecord_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
		ctx, cancel := context.WithCancel(req.Context())
		defer cancel()
@@ -6689,6 +6860,66 @@ func RegisterNakamaHandlerClient(ctx context.Context, mux *runtime.ServeMux, cli

	})

	mux.Handle("POST", pattern_Nakama_ValidatePurchaseApple_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
		ctx, cancel := context.WithCancel(req.Context())
		defer cancel()
		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
		rctx, err := runtime.AnnotateContext(ctx, mux, req, "/nakama.api.Nakama/ValidatePurchaseApple")
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}
		resp, md, err := request_Nakama_ValidatePurchaseApple_0(rctx, inboundMarshaler, client, req, pathParams)
		ctx = runtime.NewServerMetadataContext(ctx, md)
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}

		forward_Nakama_ValidatePurchaseApple_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)

	})

	mux.Handle("POST", pattern_Nakama_ValidatePurchaseGoogle_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
		ctx, cancel := context.WithCancel(req.Context())
		defer cancel()
		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
		rctx, err := runtime.AnnotateContext(ctx, mux, req, "/nakama.api.Nakama/ValidatePurchaseGoogle")
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}
		resp, md, err := request_Nakama_ValidatePurchaseGoogle_0(rctx, inboundMarshaler, client, req, pathParams)
		ctx = runtime.NewServerMetadataContext(ctx, md)
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}

		forward_Nakama_ValidatePurchaseGoogle_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)

	})

	mux.Handle("POST", pattern_Nakama_ValidatePurchaseHuawei_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
		ctx, cancel := context.WithCancel(req.Context())
		defer cancel()
		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
		rctx, err := runtime.AnnotateContext(ctx, mux, req, "/nakama.api.Nakama/ValidatePurchaseHuawei")
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}
		resp, md, err := request_Nakama_ValidatePurchaseHuawei_0(rctx, inboundMarshaler, client, req, pathParams)
		ctx = runtime.NewServerMetadataContext(ctx, md)
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}

		forward_Nakama_ValidatePurchaseHuawei_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)

	})

	mux.Handle("POST", pattern_Nakama_WriteLeaderboardRecord_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
		ctx, cancel := context.WithCancel(req.Context())
		defer cancel()
@@ -6893,6 +7124,12 @@ var (

	pattern_Nakama_UpdateGroup_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v2", "group", "group_id"}, ""))

	pattern_Nakama_ValidatePurchaseApple_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "iap", "purchase", "apple"}, ""))

	pattern_Nakama_ValidatePurchaseGoogle_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "iap", "purchase", "google"}, ""))

	pattern_Nakama_ValidatePurchaseHuawei_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "iap", "purchase", "huawei"}, ""))

	pattern_Nakama_WriteLeaderboardRecord_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v2", "leaderboard", "leaderboard_id"}, ""))

	pattern_Nakama_WriteStorageObjects_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v2", "storage"}, ""))
@@ -7041,6 +7278,12 @@ var (

	forward_Nakama_UpdateGroup_0 = runtime.ForwardResponseMessage

	forward_Nakama_ValidatePurchaseApple_0 = runtime.ForwardResponseMessage

	forward_Nakama_ValidatePurchaseGoogle_0 = runtime.ForwardResponseMessage

	forward_Nakama_ValidatePurchaseHuawei_0 = runtime.ForwardResponseMessage

	forward_Nakama_WriteLeaderboardRecord_0 = runtime.ForwardResponseMessage

	forward_Nakama_WriteStorageObjects_0 = runtime.ForwardResponseMessage
+24 −0
Original line number Diff line number Diff line
@@ -643,6 +643,30 @@ service Nakama {
    };
  }

  // Validate Apple IAP Receipt
  rpc ValidatePurchaseApple (api.ValidatePurchaseAppleRequest) returns (api.ValidatePurchaseResponse) {
    option (google.api.http) = {
      post: "/v2/iap/purchase/apple",
      body: "*"
    };
  }

  // Validate Google IAP Receipt
  rpc ValidatePurchaseGoogle (api.ValidatePurchaseGoogleRequest) returns (api.ValidatePurchaseResponse) {
    option (google.api.http) = {
      post: "/v2/iap/purchase/google",
      body: "*"
    };
  }

  // Validate Huawei IAP Receipt
  rpc ValidatePurchaseHuawei (api.ValidatePurchaseHuaweiRequest) returns (api.ValidatePurchaseResponse) {
    option (google.api.http) = {
      post: "/v2/iap/purchase/huawei",
      body: "*"
    };
  }

  // Write a record to a leaderboard.
  rpc WriteLeaderboardRecord (api.WriteLeaderboardRecordRequest) returns (api.LeaderboardRecord) {
    option (google.api.http) = {
+209 −0
Original line number Diff line number Diff line
@@ -2055,6 +2055,105 @@
        ]
      }
    },
    "/v2/iap/purchase/apple": {
      "post": {
        "summary": "Validate Apple IAP Receipt",
        "operationId": "Nakama_ValidatePurchaseApple",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/apiValidatePurchaseResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/apiValidatePurchaseAppleRequest"
            }
          }
        ],
        "tags": [
          "Nakama"
        ]
      }
    },
    "/v2/iap/purchase/google": {
      "post": {
        "summary": "Validate Google IAP Receipt",
        "operationId": "Nakama_ValidatePurchaseGoogle",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/apiValidatePurchaseResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/apiValidatePurchaseGoogleRequest"
            }
          }
        ],
        "tags": [
          "Nakama"
        ]
      }
    },
    "/v2/iap/purchase/huawei": {
      "post": {
        "summary": "Validate Huawei IAP Receipt",
        "operationId": "Nakama_ValidatePurchaseHuawei",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/apiValidatePurchaseResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/apiValidatePurchaseHuaweiRequest"
            }
          }
        ],
        "tags": [
          "Nakama"
        ]
      }
    },
    "/v2/leaderboard/{leaderboardId}": {
      "get": {
        "summary": "List leaderboard records.",
@@ -3140,6 +3239,28 @@
      },
      "description": "A single group-role pair."
    },
    "ValidatedPurchaseEnvironment": {
      "type": "string",
      "enum": [
        "UNKNOWN",
        "SANDBOX",
        "PRODUCTION"
      ],
      "default": "UNKNOWN",
      "description": "- UNKNOWN: Unknown environment.\n - SANDBOX: Sandbox/test environment.\n - PRODUCTION: Production environment.",
      "title": "Environment where the purchase took place"
    },
    "ValidatedPurchaseStore": {
      "type": "string",
      "enum": [
        "APPLE_APP_STORE",
        "GOOGLE_PLAY_STORE",
        "HUAWEI_APP_GALLERY"
      ],
      "default": "APPLE_APP_STORE",
      "description": "- APPLE_APP_STORE: Apple App Store\n - GOOGLE_PLAY_STORE: Google Play Store\n - HUAWEI_APP_GALLERY: Huawei App Gallery",
      "title": "Validation Provider"
    },
    "WriteLeaderboardRecordRequestLeaderboardRecordWrite": {
      "type": "object",
      "properties": {
@@ -4413,6 +4534,94 @@
      },
      "description": "A collection of zero or more users."
    },
    "apiValidatePurchaseAppleRequest": {
      "type": "object",
      "properties": {
        "receipt": {
          "type": "string",
          "description": "Base64 encoded Apple receipt data payload."
        }
      },
      "title": "Apple IAP Purchases validation request"
    },
    "apiValidatePurchaseGoogleRequest": {
      "type": "object",
      "properties": {
        "purchase": {
          "type": "string",
          "description": "JSON encoded Google purchase payload."
        }
      },
      "title": "Google IAP Purchase validation request"
    },
    "apiValidatePurchaseHuaweiRequest": {
      "type": "object",
      "properties": {
        "purchase": {
          "type": "string",
          "description": "JSON encoded Huawei InAppPurchaseData."
        },
        "signature": {
          "type": "string",
          "description": "InAppPurchaseData signature."
        }
      },
      "title": "Huawei IAP Purchase validation request"
    },
    "apiValidatePurchaseResponse": {
      "type": "object",
      "properties": {
        "validatedPurchases": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/apiValidatedPurchase"
          },
          "description": "Newly seen validated purchases."
        }
      },
      "title": "Validate IAP response"
    },
    "apiValidatedPurchase": {
      "type": "object",
      "properties": {
        "productId": {
          "type": "string",
          "description": "Purchase Product ID."
        },
        "transactionId": {
          "type": "string",
          "description": "Purchase Transaction ID."
        },
        "store": {
          "$ref": "#/definitions/ValidatedPurchaseStore",
          "title": "Store identifier"
        },
        "purchaseTime": {
          "type": "string",
          "format": "date-time",
          "description": "UNIX Timestamp when the purchase was done."
        },
        "createTime": {
          "type": "string",
          "format": "date-time",
          "description": "UNIX Timestamp when the receipt validation was stored in DB."
        },
        "updateTime": {
          "type": "string",
          "format": "date-time",
          "description": "UNIX Timestamp when the receipt validation was updated in DB."
        },
        "providerPayload": {
          "type": "string",
          "description": "Raw provider validation response."
        },
        "environment": {
          "$ref": "#/definitions/ValidatedPurchaseEnvironment",
          "description": "Whether the purchase was done in production or sandbox environment."
        }
      },
      "description": "Validated Purchase stored by Nakama."
    },
    "apiWriteStorageObject": {
      "type": "object",
      "properties": {
+114 −0

File changed.

Preview size limit exceeded, changes collapsed.

Loading